You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
####Microsoft Entra ID / Azure Active Directory Authentication:
9
+
### Microsoft Entra ID / Azure Active Directory Authentication:
12
10
```
13
11
pip install "sqlmesh[mssql-odbc]"
14
12
```
15
13
14
+
## Incremental by unique key `MERGE`
15
+
16
+
SQLMesh executes a `MERGE` statement to insert rows for [incremental by unique key](../../concepts/models/model_kinds.md#incremental_by_unique_key) model kinds.
17
+
18
+
By default, the `MERGE` statement updates all non-key columns of an existing row when a new row with the same key values is inserted. If all column values match between the two rows, those updates are unnecessary.
19
+
20
+
SQLMesh provides an optional performance optimization that skips unnecessary updates by comparing column values with the `EXISTS` and `EXCEPT` operators.
21
+
22
+
Enable the optimization by setting the `mssql_merge_exists` key to `true` in the [`physical_properties`](../../concepts/models/overview.md#physical_properties) section of the `MODEL` statement.
23
+
24
+
For example:
25
+
26
+
```sql linenums="1" hl_lines="7-9"
27
+
MODEL (
28
+
name sqlmesh_example.unique_key,
29
+
kind INCREMENTAL_BY_UNIQUE_KEY (
30
+
unique_key id
31
+
),
32
+
cron '@daily',
33
+
physical_properties (
34
+
mssql_merge_exists = true
35
+
)
36
+
);
37
+
```
38
+
39
+
!!! warning "Not all column types supported"
40
+
The `mssql_merge_exists` optimization is not supported for all column types, including `GEOMETRY`, `XML`, `TEXT`, `NTEXT`, `IMAGE`, and most user-defined types.
41
+
42
+
Learn more in the [MSSQL `EXCEPT` statement documentation](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/set-operators-except-and-intersect-transact-sql?view=sql-server-ver17#arguments).
|`type`| Engine type name - must be `mssql`| string | Y |
21
-
|`host`| The hostname of the MSSQL server | string | Y |
22
-
|`user`| The username / client id to use for authentication with the MSSQL server | string | N |
23
-
|`password`| The password / client secret to use for authentication with the MSSQL server | string | N |
24
-
|`port`| The port number of the MSSQL server | int | N |
25
-
|`database`| The target database | string | N |
26
-
|`charset`| The character set used for the connection | string | N |
27
-
|`timeout`| The query timeout in seconds. Default: no timeout | int | N |
28
-
|`login_timeout`| The timeout for connection and login in seconds. Default: 60 | int | N |
29
-
|`appname`| The application name to use for the connection | string | N |
30
-
|`conn_properties`| The list of connection properties | list[string]| N |
31
-
|`autocommit`| Is autocommit mode enabled. Default: false | bool | N |
32
-
|`driver`| The driver to use for the connection. Default: pymssql | string | N |
33
-
|`driver_name`| The driver name to use for the connection. E.g., *ODBC Driver 18 for SQL Server*| string | N |
34
-
|`odbc_properties`|The dict of ODBC connection properties. E.g., authentication: ActiveDirectoryServicePrincipal. See more [here](https://learn.microsoft.com/en-us/sql/connect/odbc/dsn-connection-string-attribute?view=sql-server-ver16). | dict | N |
|`type`| Engine type name - must be `mssql`| string | Y |
52
+
|`host`| The hostname of the MSSQL server | string | Y |
53
+
|`user`| The username / client id to use for authentication with the MSSQL server | string | N |
54
+
|`password`| The password / client secret to use for authentication with the MSSQL server | string | N |
55
+
|`port`| The port number of the MSSQL server | int | N |
56
+
|`database`| The target database | string | N |
57
+
|`charset`| The character set used for the connection | string | N |
58
+
|`timeout`| The query timeout in seconds. Default: no timeout | int | N |
59
+
|`login_timeout`| The timeout for connection and login in seconds. Default: 60 | int | N |
60
+
|`appname`| The application name to use for the connection | string | N |
61
+
|`conn_properties`| The list of connection properties | list[string]| N |
62
+
|`autocommit`| Is autocommit mode enabled. Default: false | bool | N |
63
+
|`driver`| The driver to use for the connection. Default: pymssql| string | N |
64
+
|`driver_name`| The driver name to use for the connection (e.g., *ODBC Driver 18 for SQL Server*). | string | N |
65
+
|`odbc_properties`| ODBC connection properties (e.g., *authentication: ActiveDirectoryServicePrincipal*). See more [here](https://learn.microsoft.com/en-us/sql/connect/odbc/dsn-connection-string-attribute?view=sql-server-ver16). |dict| N |
Copy file name to clipboardExpand all lines: tests/core/engine_adapter/test_mssql.py
+62-6Lines changed: 62 additions & 6 deletions
Original file line number
Diff line number
Diff line change
@@ -474,7 +474,7 @@ def test_merge_pandas(
474
474
475
475
assertto_sql_calls(adapter) == [
476
476
f"""IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '__temp_target_{temp_table_id}') EXEC('CREATE TABLE [__temp_target_{temp_table_id}] ([id] INTEGER, [ts] DATETIME2, [val] INTEGER)');""",
477
-
f"MERGE INTO [target] AS [__MERGE_TARGET__] USING (SELECT CAST([id] AS INTEGER) AS [id], CAST([ts] AS DATETIME2) AS [ts], CAST([val] AS INTEGER) AS [val] FROM [__temp_target_{temp_table_id}]) AS [__MERGE_SOURCE__] ON [__MERGE_TARGET__].[id] = [__MERGE_SOURCE__].[id] WHEN MATCHED AND EXISTS(SELECT [__MERGE_TARGET__].[ts], [__MERGE_TARGET__].[val] EXCEPT SELECT [__MERGE_SOURCE__].[ts], [__MERGE_SOURCE__].[val]) THEN UPDATE SET [__MERGE_TARGET__].[ts] = [__MERGE_SOURCE__].[ts], [__MERGE_TARGET__].[val] = [__MERGE_SOURCE__].[val] WHEN NOT MATCHED THEN INSERT ([id], [ts], [val]) VALUES ([__MERGE_SOURCE__].[id], [__MERGE_SOURCE__].[ts], [__MERGE_SOURCE__].[val]);",
477
+
f"MERGE INTO [target] AS [__MERGE_TARGET__] USING (SELECT CAST([id] AS INTEGER) AS [id], CAST([ts] AS DATETIME2) AS [ts], CAST([val] AS INTEGER) AS [val] FROM [__temp_target_{temp_table_id}]) AS [__MERGE_SOURCE__] ON [__MERGE_TARGET__].[id] = [__MERGE_SOURCE__].[id] WHEN MATCHED THEN UPDATE SET [__MERGE_TARGET__].[ts] = [__MERGE_SOURCE__].[ts], [__MERGE_TARGET__].[val] = [__MERGE_SOURCE__].[val] WHEN NOT MATCHED THEN INSERT ([id], [ts], [val]) VALUES ([__MERGE_SOURCE__].[id], [__MERGE_SOURCE__].[ts], [__MERGE_SOURCE__].[val]);",
478
478
f"DROP TABLE IF EXISTS [__temp_target_{temp_table_id}];",
479
479
]
480
480
@@ -498,11 +498,47 @@ def test_merge_pandas(
498
498
499
499
assertto_sql_calls(adapter) == [
500
500
f"""IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '__temp_target_{temp_table_id}') EXEC('CREATE TABLE [__temp_target_{temp_table_id}] ([id] INTEGER, [ts] DATETIME2, [val] INTEGER)');""",
501
-
f"MERGE INTO [target] AS [__MERGE_TARGET__] USING (SELECT CAST([id] AS INTEGER) AS [id], CAST([ts] AS DATETIME2) AS [ts], CAST([val] AS INTEGER) AS [val] FROM [__temp_target_{temp_table_id}]) AS [__MERGE_SOURCE__] ON [__MERGE_TARGET__].[id] = [__MERGE_SOURCE__].[id] AND [__MERGE_TARGET__].[ts] = [__MERGE_SOURCE__].[ts] WHEN MATCHED AND EXISTS(SELECT [__MERGE_TARGET__].[val] EXCEPT SELECT [__MERGE_SOURCE__].[val]) THEN UPDATE SET [__MERGE_TARGET__].[val] = [__MERGE_SOURCE__].[val] WHEN NOT MATCHED THEN INSERT ([id], [ts], [val]) VALUES ([__MERGE_SOURCE__].[id], [__MERGE_SOURCE__].[ts], [__MERGE_SOURCE__].[val]);",
501
+
f"MERGE INTO [target] AS [__MERGE_TARGET__] USING (SELECT CAST([id] AS INTEGER) AS [id], CAST([ts] AS DATETIME2) AS [ts], CAST([val] AS INTEGER) AS [val] FROM [__temp_target_{temp_table_id}]) AS [__MERGE_SOURCE__] ON [__MERGE_TARGET__].[id] = [__MERGE_SOURCE__].[id] AND [__MERGE_TARGET__].[ts] = [__MERGE_SOURCE__].[ts] WHEN MATCHED THEN UPDATE SET [__MERGE_TARGET__].[val] = [__MERGE_SOURCE__].[val] WHEN NOT MATCHED THEN INSERT ([id], [ts], [val]) VALUES ([__MERGE_SOURCE__].[id], [__MERGE_SOURCE__].[ts], [__MERGE_SOURCE__].[val]);",
502
+
f"DROP TABLE IF EXISTS [__temp_target_{temp_table_id}];",
f"""IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '__temp_target_{temp_table_id}') EXEC('CREATE TABLE [__temp_target_{temp_table_id}] ([id] INTEGER, [ts] DATETIME2, [val] INTEGER)');""",
537
+
f"MERGE INTO [target] AS [__MERGE_TARGET__] USING (SELECT CAST([id] AS INTEGER) AS [id], CAST([ts] AS DATETIME2) AS [ts], CAST([val] AS INTEGER) AS [val] FROM [__temp_target_{temp_table_id}]) AS [__MERGE_SOURCE__] ON [__MERGE_TARGET__].[id] = [__MERGE_SOURCE__].[id] WHEN MATCHED THEN UPDATE SET [__MERGE_TARGET__].[ts] = [__MERGE_SOURCE__].[ts], [__MERGE_TARGET__].[val] = [__MERGE_SOURCE__].[val] WHEN NOT MATCHED THEN INSERT ([id], [ts], [val]) VALUES ([__MERGE_SOURCE__].[id], [__MERGE_SOURCE__].[ts], [__MERGE_SOURCE__].[val]);",
502
538
f"DROP TABLE IF EXISTS [__temp_target_{temp_table_id}];",
f"""IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '__temp_target_{temp_table_id}') EXEC('CREATE TABLE [__temp_target_{temp_table_id}] ([id] INTEGER, [ts] DATETIME2, [val] INTEGER)');""",
559
+
f"MERGE INTO [target] AS [__MERGE_TARGET__] USING (SELECT CAST([id] AS INTEGER) AS [id], CAST([ts] AS DATETIME2) AS [ts], CAST([val] AS INTEGER) AS [val] FROM [__temp_target_{temp_table_id}]) AS [__MERGE_SOURCE__] ON [__MERGE_TARGET__].[id] = [__MERGE_SOURCE__].[id] WHEN MATCHED AND EXISTS(SELECT [__MERGE_TARGET__].[ts], [__MERGE_TARGET__].[val] EXCEPT SELECT [__MERGE_SOURCE__].[ts], [__MERGE_SOURCE__].[val]) THEN UPDATE SET [__MERGE_TARGET__].[ts] = [__MERGE_SOURCE__].[ts], [__MERGE_TARGET__].[val] = [__MERGE_SOURCE__].[val] WHEN NOT MATCHED THEN INSERT ([id], [ts], [val]) VALUES ([__MERGE_SOURCE__].[id], [__MERGE_SOURCE__].[ts], [__MERGE_SOURCE__].[val]);",
560
+
f"DROP TABLE IF EXISTS [__temp_target_{temp_table_id}];",
0 commit comments