@@ -716,4 +716,136 @@ public void UpsertTrackedAsInsert()
716716 }
717717
718718 #endregion
719+
720+ #region MERGE INTO 测试
721+
722+ [ Fact ( DisplayName = "MERGE INTO 行不存在时执行插入" ) ]
723+ public void MergeInsertWhenNotExists ( )
724+ {
725+ CreateUsersTable ( ) ;
726+
727+ var result = _engine . Execute ( "MERGE INTO users (id, name, age) VALUES (1, 'Alice', 25)" ) ;
728+ Assert . Equal ( 1 , result . AffectedRows ) ;
729+
730+ var select = _engine . Execute ( "SELECT * FROM users WHERE id = 1" ) ;
731+ Assert . Single ( select . Rows ! ) ;
732+ Assert . Equal ( "Alice" , select . Rows ! [ 0 ] [ 1 ] ) ;
733+ Assert . Equal ( 25 , select . Rows ! [ 0 ] [ 2 ] ) ;
734+ }
735+
736+ [ Fact ( DisplayName = "MERGE INTO 主键冲突时执行更新" ) ]
737+ public void MergeUpdateWhenPkConflict ( )
738+ {
739+ SeedUsers ( ) ;
740+
741+ var result = _engine . Execute ( "MERGE INTO users VALUES (1, 'AliceUpdated', 99)" ) ;
742+ Assert . Equal ( 1 , result . AffectedRows ) ;
743+
744+ var select = _engine . Execute ( "SELECT * FROM users WHERE id = 1" ) ;
745+ Assert . Single ( select . Rows ! ) ;
746+ Assert . Equal ( "AliceUpdated" , select . Rows ! [ 0 ] [ 1 ] ) ;
747+ Assert . Equal ( 99 , select . Rows ! [ 0 ] [ 2 ] ) ;
748+ }
749+
750+ [ Fact ( DisplayName = "MERGE INTO 不指定列名" ) ]
751+ public void MergeWithoutColumns ( )
752+ {
753+ SeedUsers ( ) ;
754+
755+ _engine . Execute ( "MERGE INTO users VALUES (1, 'Updated', 88)" ) ;
756+
757+ var select = _engine . Execute ( "SELECT * FROM users WHERE id = 1" ) ;
758+ Assert . Single ( select . Rows ! ) ;
759+ Assert . Equal ( "Updated" , select . Rows ! [ 0 ] [ 1 ] ) ;
760+ Assert . Equal ( 88 , select . Rows ! [ 0 ] [ 2 ] ) ;
761+ }
762+
763+ [ Fact ( DisplayName = "MERGE INTO 指定部分列名" ) ]
764+ public void MergeWithPartialColumns ( )
765+ {
766+ SeedUsers ( ) ;
767+
768+ _engine . Execute ( "MERGE INTO users (id, name) VALUES (1, 'NewName')" ) ;
769+
770+ var select = _engine . Execute ( "SELECT * FROM users WHERE id = 1" ) ;
771+ Assert . Single ( select . Rows ! ) ;
772+ Assert . Equal ( "NewName" , select . Rows ! [ 0 ] [ 1 ] ) ;
773+ // age 保持原值
774+ Assert . Equal ( 25 , select . Rows ! [ 0 ] [ 2 ] ) ;
775+ }
776+
777+ [ Fact ( DisplayName = "MERGE INTO 多行混合插入和更新" ) ]
778+ public void MergeMultipleRowsMixed ( )
779+ {
780+ SeedUsers ( ) ;
781+
782+ // id=1 已存在 → 更新;id=10 不存在 → 插入
783+ var result = _engine . Execute ( "MERGE INTO users VALUES (1, 'AliceNew', 50), (10, 'Dave', 40)" ) ;
784+ Assert . Equal ( 2 , result . AffectedRows ) ;
785+
786+ var select1 = _engine . Execute ( "SELECT * FROM users WHERE id = 1" ) ;
787+ Assert . Equal ( "AliceNew" , select1 . Rows ! [ 0 ] [ 1 ] ) ;
788+ Assert . Equal ( 50 , select1 . Rows ! [ 0 ] [ 2 ] ) ;
789+
790+ var select2 = _engine . Execute ( "SELECT * FROM users WHERE id = 10" ) ;
791+ Assert . Single ( select2 . Rows ! ) ;
792+ Assert . Equal ( "Dave" , select2 . Rows ! [ 0 ] [ 1 ] ) ;
793+ }
794+
795+ [ Fact ( DisplayName = "MERGE INTO 唯一索引冲突时执行更新" ) ]
796+ public void MergeUpdateOnUniqueIndexConflict ( )
797+ {
798+ CreateUsersTable ( ) ;
799+ _engine . Execute ( "CREATE UNIQUE INDEX idx_name ON users (name)" ) ;
800+ _engine . Execute ( "INSERT INTO users VALUES (1, 'Alice', 25)" ) ;
801+
802+ // 唯一索引冲突:name='Alice' 已存在
803+ var result = _engine . Execute ( "MERGE INTO users VALUES (99, 'Alice', 99)" ) ;
804+ Assert . Equal ( 1 , result . AffectedRows ) ;
805+
806+ // 应该更新已有行的 age,而非插入新行
807+ var select = _engine . Execute ( "SELECT * FROM users WHERE id = 1" ) ;
808+ Assert . Single ( select . Rows ! ) ;
809+ Assert . Equal ( "Alice" , select . Rows ! [ 0 ] [ 1 ] ) ;
810+ Assert . Equal ( 99 , select . Rows ! [ 0 ] [ 2 ] ) ;
811+
812+ // 不应有 id=99 的行
813+ var select2 = _engine . Execute ( "SELECT * FROM users WHERE id = 99" ) ;
814+ Assert . Empty ( select2 . Rows ! ) ;
815+ }
816+
817+ [ Fact ( DisplayName = "MERGE INTO 使用参数化值" ) ]
818+ public void MergeWithParameters ( )
819+ {
820+ SeedUsers ( ) ;
821+
822+ var parameters = new Dictionary < String , Object ? >
823+ {
824+ [ "@id" ] = 1 ,
825+ [ "@name" ] = "ParamAlice" ,
826+ [ "@age" ] = 77
827+ } ;
828+
829+ var result = _engine . Execute (
830+ "MERGE INTO users (id, name, age) VALUES (@id, @name, @age)" ,
831+ parameters ) ;
832+ Assert . Equal ( 1 , result . AffectedRows ) ;
833+
834+ var select = _engine . Execute ( "SELECT * FROM users WHERE id = 1" ) ;
835+ Assert . Equal ( "ParamAlice" , select . Rows ! [ 0 ] [ 1 ] ) ;
836+ Assert . Equal ( 77 , select . Rows ! [ 0 ] [ 2 ] ) ;
837+ }
838+
839+ [ Fact ( DisplayName = "MERGE INTO 计入 InsertCount" ) ]
840+ public void MergeTrackedAsInsert ( )
841+ {
842+ CreateUsersTable ( ) ;
843+ var beforeInsert = _engine . Metrics . InsertCount ;
844+
845+ _engine . Execute ( "MERGE INTO users VALUES (1, 'Alice', 25)" ) ;
846+
847+ Assert . Equal ( beforeInsert + 1 , _engine . Metrics . InsertCount ) ;
848+ }
849+
850+ #endregion
719851}
0 commit comments