Azure Synapse Dedicated SQL Pool Parse Error - azure-synapse

When execute the script below on Azure Synapse Dedicated SQL Pool I get the error:
Parse error at line: 11, column: 1: Incorrect syntax near 'FOR'.
However, I if execute the same code on Azure Synapse Serverless Pool it works fine.
DECLARE
#EnrichedViewDatabase sysname,
#EnrichedViewSchema sysname,
#EnrichedColumnSuffix varchar(50),
#LanguageCode varchar(10),
#BaseTableSuffix varchar(50),
#PreviewOnly bit, --Indicate whether to preview the SQL Script (without creating the views) = 1 ; Create views = 0;
#CurrentDatabase sysname,
#CurrentDatabaseSchema sysname
SET #EnrichedViewDatabase = 'DedicatedSQLPool'
SET #EnrichedViewSchema = 'dbo'
SET #EnrichedColumnSuffix = 'code'
SET #LanguageCode = 1033
SET #BaseTableSuffix = ''
SET #PreviewOnly = 0
SET #CurrentDatabase = 'dataverse_montaguqa1_unq5a4857c03d0545c6b9ad616ca59a7'
SET #CurrentDatabaseSchema = 'dbo'
DECLARE #ColumnMetadata nvarchar(MAX), #ColumnMetadataSQL nvarchar(MAX)
--Define the SQL statement to retrieve column metadata from the Lake Database managed by Synapse Link for Dataverse
--Results will be stored as a JSON document in a variable
SET #ColumnMetadataSQL = 'SET #ColumnMetadataOUT = (
SELECT TABLE_SCHEMA,
TABLE_NAME,
COLUMN_NAME,
ORDINAL_POSITION,
DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = ''dbo''
AND TABLE_NAME NOT IN (''OptionsetMetadata'', ''GlobalOptionsetMetadata'',''StateMetadata'',''StatusMetadata'', ''TargetMetadata'')
AND TABLE_NAME LIKE ''%' + #BaseTableSuffix + '''
FOR JSON AUTO)'
DECLARE #ParmDefinition NVARCHAR(MAX);
SET #ParmDefinition = N'#ColumnMetadataOUT NVARCHAR(MAX) OUTPUT';
EXECUTE sp_executesql #ColumnMetadataSQL, #ParmDefinition, #ColumnMetadataOUT=#ColumnMetadata OUTPUT;
--Declare a variable to store a SQL statement for creating enriched views
DECLARE #SQL nvarchar(MAX) = ''
Any thoughts

For Clause is available in SQL server, Azure SQL Database, Azure SQL MI.
It is not supported in Azure Synapse Analytics.
Image Reference:
FOR Clause (Transact-SQL) - SQL Server | Microsoft Learn
In synapse dedicated SQL pool, you can use concat and string_agg functions and create Json data manually instead of using For JSON auto. I tried this with sample data.
Sample source data:
Query to convert the table data in Json format:
select concat('{',string_agg(concat('{id:"',id,'",name:"',name,'"}'),','),'}')
as JSON_data from tab1
Result:
You can log a feature request here.

Related

SQL server linked server update

Currently working on a script which can update a number of different tables across linked servers. I use 08/12 SQL server management studio.
I am having an issue where I have tried to set up the linked server string as a variable, so I can set this at the beginning and reference when needed through the script. I have been working on the assumption I can declare this as a local variable, as I have for other parts of my script that are updating the local table/server, but when running the script, I receive an incorrect syntax error for the section of code my linked server string variable is located.
My code is structured as:
declare #string varchar(max)
set #string = '[server,instance].[database].dbo.table1'
update #string
set field = updatevariable
where record = identifier
Is it possible to use a linked server as a variable?
You can dynamically construct SQL Update string, and then pass it to exec. Example:
declare
#srv nvarchar(max) = N'[server].[db].[dbo].[table]',
#id int = 100,
#value nvarchar(max) = N'some value',
#sql nvarchar(max)
set #sql = N'update ' + #srv
+ ' set field = ''' + #value + ''' '
+ ' where record = ' + cast(#id as nvarchar)
exec(#sql)

How to check are there JSON Functions by SQL query?

There are JSON Function in SQL 2016 like JSON_VALUE, JSON_QUERY and other.
I would like to use it in my queries, but I still have old servers with SQL 2014, for example, that are not allowed to use the new functionality.
Can I check are there functions like JSON_VALUE by query? Something like
IF operator_exists('JSON_VALUE')
SELECT JSON_VALUE([Value], '$.MyPath')
FROM [dbo].[MyTable]
WHERE [Name] = 'MyProperty'
ELSE
SELECT ''
Thanks.
UPDATE
If I use ckecking like this (thanks Rigerta Demiri)
DECLARE #compatibility_level int
SELECT #compatibility_level= compatibility_level FROM sys.databases WHERE name = 'MyDbName'
IF (#compatibility_level >= 130)
BEGIN
SELECT JSON_VALUE([Value], '$.MyPath')
FROM [dbo].[MyTable]
WHERE [Name] = 'MyProperty'
END
SELECT 'not allowed'
... I get the following SQL exception (on 2014 SQL Studio):
'JSON_VALUE' is not a recognized built-in function name
May be 2014 MSSQL interpretator try to parse all blocks of code and cannot understand what is JSON_VALUE?
Since it depends on the version of SQL Server that you have installed and since you have different instances (even older ones than SQL Server 2016) you can just check if the compatibility level of the database where you are trying to query is equal to 130.
You could do the following:
declare #compatibility_level int
select #compatibility_level= compatibility_level from sys.databases where name = 'TestDB'
if (#compatibility_level >= 130)
begin
declare #jsoninfo nvarchar(max)
set #jsoninfo=N'{
"info":{
"type":1,
"address":{
"town":"bristol",
"county":"avon",
"country":"england"
},
"tags":["sport", "water polo"]
},
"type":"basic"
}'
select json_value(#jsoninfo,'$.info.address.town') as town
end
The OPENJSON function is available only under compatibility level 130
(or higher).
as you can read in the documentation.
EDIT:
What you got happens because apparently "SQL Server doesn't know or care which branch of a conditional will be entered; it validates all of the statements in a batch anyway." as stated in the answer of this post: T-Sql appears to be evaluating “If” statement even when the condition is not true.
So, the workaround would be to create the whole statement as a dynamic string.
Like this:
declare #compatibility_level int
select #compatibility_level= compatibility_level from sys.databases where name = 'TradingDWH'
if (#compatibility_level >= 130)
begin
declare #sql nvarchar(max);
set #sql = ' declare #jsoninfo nvarchar(max) ' + ' set #jsoninfo=N''{ "info":{' + ' "type":1, "address":{ "town":"bristol", "county":"avon", "country":"england" }, "tags":["sport", "water polo"] }, "type":"basic" }'
set #sql = #sql + 'select json_value(#jsoninfo,''$.info.address.town'') as town'
select #sql
--exec sp_executesql #sql
-- or your own query, like this:
declare #sql2 nvarchar(max);
declare #MyProperty nvarchar(100) = 'YourProperty'
set #sql2 = ' SELECT JSON_VALUE([Value], ''$.MyPath'') '
set #sql2 = #sql2 + 'FROM [dbo].[MyTable] WHERE [Name] = #MyProperty '
select #sql2
--exec sp_executesql #sql2, N'#MyProperty nvarchar(100)', #MyProperty
end
else
begin
select 'Version prior to 130!' as [message]
end
One of many resources where you can read more about dynamic SQL is Don’t Fear Dynamic SQL.

Create database scoped credential syntax

Working on a DB project in VS2015 (Azure SQL V12 proj). I need to use an external table reference so I have to create a credential object to authenticate with the remote server. Code and error below, not sure what I am missing.
SQL code
CREATE DATABASE SCOPED CREDENTIAL [mycredential]
WITH
IDENTITY = 'SomeIdentity',
SECRET = 'SomeSecret';
Errors:
Incorrect syntax near '[mycredential]'. Expecting '='
Incorrect syntax near 'IDENTITY'. Expecting AW_CHANGE_TRACKING_CONTEXT, AW_ID,
AW_XMLNAMESPACES, or QUOTED_ID.
Ok, I also encountered this in VS2017 DB project, the way I did it is to use stored procs, so that intellisense will not trigger an error. As i find the code is working when run. Below is the stored proc I used:
you define your external reference table in "YOUR_EXTERN_TABLE" of "CREATE EXTERNAL TABLE" statement (which, in this example, is set to have schema of ID and Name columns):
CREATE PROCEDURE [dbo].[StoredProcWithExternalRefs]
#DatabaseName AS NVARCHAR(30),
#Passw AS NVARCHAR(100),
#SaID AS NVARCHAR(100),
#DataSource AS NVARCHAR(512),
#Catalog AS NVARCHAR(200)
AS
BEGIN
SET NOCOUNT ON;
SET IMPLICIT_TRANSACTIONS OFF;
DECLARE #SQLString nvarchar(200);
PRINT 'START'
PRINT 'CREATE DATABASE'
SET #SQLString = N'CREATE DATABASE [' + #DatabaseName + ']'
EXECUTE sp_executesql #SQLString
SET #SQLString = N'CREATE MASTER KEY ENCRYPTION BY PASSWORD = ''' + #Passw + ''';
CREATE DATABASE SCOPED CREDENTIAL credential_name
WITH IDENTITY = ''' + #SaID + '''
, SECRET = ''' + #Passw + ''';
CREATE EXTERNAL DATA SOURCE RemoteReferenceData
WITH
(
TYPE=RDBMS,
LOCATION=''' + #DataSource + ''',
DATABASE_NAME=''' + #Catalog + ''',
CREDENTIAL= credential_name
);
CREATE EXTERNAL TABLE YOUR_EXTERN_TABLE(
[Id] [int] NOT NULL,
[Name] [nvarchar](20) NOT NULL,
) WITH ( DATA_SOURCE = RemoteReferenceData );'
...
EXECUTE sp_executesql #SQLString
PRINT 'DONE.'
END
you can add additional external tables with the same pattern in the "CREATE EXTERNAL TABLE" statement and the schema.
here is a reference to guide you: https://sqldusty.com/2017/05/30/setting-up-cross-database-queries-in-azure-sql-database/

SQL Server 2008 R2 error in generating the script

I am trying to generated the script of a database on SQL Server 2008 R2
I got this error
Microsoft.SqlServer.Management.Smo.PropertyCannotBeRetrievedException: Property TextHeader is not available for StoredProcedure '[dbo].[p_SYS_GetQDNPassword]'. This property may not exist for this object, or may not be retrievable due to insufficient access rights. The text is encrypted. at Microsoft.SqlServer.Management.SqlScriptPublish.GeneratePublishPage.worker_DoWork(Object sender, DoWorkEventArgs e) at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e) at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
Could you help please?
I don't know what other information you need, but whatever you need, tell me please to give you.
Using the sp_helptext dbo.p_SYS_GetQDNPassword should give you the result:
"The text for object 'YourProcedureName' is encrypted."
This means that stored procedure is created using WITH ENCRYPTION, and SQL Server internally stores the text with the definition of the object in an obfuscated format
The actual definition of an object is stored in system table sys.sysobjvalues which is not directly accessible. By connecting to SQL Server using the Dedicated Administrator Connection (DAC) you can select the imageval column in which the information is stored
Click on Database Engine Query button:
The Connect To Database Engine dialog will pop out. Type the "admin:" prefix before the server name and you'll be connected to DAC
You can find useful info here in this article
http://www.mssqltips.com/sqlservertip/2964/encrypting-and-decrypting-sql-server-stored-procedures-views-and-userdefined-functions/
When you're connected to DAC run the following script from the article:
SET NOCOUNT ON
GO
ALTER PROCEDURE dbo.TestDecryption WITH ENCRYPTION AS
BEGIN
PRINT 'This text is going to be decrypted'
END
GO
DECLARE #encrypted NVARCHAR(MAX)
SET #encrypted = (
SELECT imageval
FROM sys.sysobjvalues
WHERE OBJECT_NAME(objid) = 'TestDecryption' )
DECLARE #encryptedLength INT
SET #encryptedLength = DATALENGTH(#encrypted) / 2
DECLARE #procedureHeader NVARCHAR(MAX)
SET #procedureHeader = N'ALTER PROCEDURE dbo.TestDecryption WITH ENCRYPTION AS '
SET #procedureHeader = #procedureHeader + REPLICATE(N'-',(#encryptedLength
- LEN(#procedureHeader)))
EXEC sp_executesql #procedureHeader
DECLARE #blankEncrypted NVARCHAR(MAX)
SET #blankEncrypted = (
SELECT imageval
FROM sys.sysobjvalues
WHERE OBJECT_NAME(objid) = 'TestDecryption' )
SET #procedureHeader = N'CREATE PROCEDURE dbo.TestDecryption WITH ENCRYPTION AS '
SET #procedureHeader = #procedureHeader + REPLICATE(N'-',(#encryptedLength
- LEN(#procedureHeader)))
DECLARE #cnt SMALLINT
DECLARE #decryptedChar NCHAR(1)
DECLARE #decryptedMessage NVARCHAR(MAX)
SET #decryptedMessage = ''
SET #cnt = 1
WHILE #cnt <> #encryptedLength
BEGIN
SET #decryptedChar =
NCHAR(
UNICODE(SUBSTRING(
#encrypted, #cnt, 1)) ^
UNICODE(SUBSTRING(
#procedureHeader, #cnt, 1)) ^
UNICODE(SUBSTRING(
#blankEncrypted, #cnt, 1))
)
SET #decryptedMessage = #decryptedMessage + #decryptedChar
SET #cnt = #cnt + 1
END
SELECT #decryptedMessage
Of course change the dbo.TestDecryption with your procedure name in the script :)
This script works for me, hope it helps
Use
sp_helptext dbo.p_SYS_GetQDNPassword
and
Try to alter the stored procedure and try to get script

T-SQL: Variable Scope

I am trying to store the results of an SQL query into a variable.The query simply detects the datatype of a column, hence the returned result is a single varchar.
SET #SQL =
'declare ##x varchar(max) SET ##x = (select DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE Table_name = ' +char(39)+#TabName+char(39) +
' AND column_name = ' +char(39)+#colName+char(39) + ')'
EXECUTE (#SQL)
Anything within the 'SET declaration' cannot access any variables outside of it and vice versa, so I am stuck on how to store the results of this query in a varchar variable to be accessed by other parts of the stored procedure.
You dont need a dynamic query to achieve what you want, below query will give the same result as yours.
declare #x varchar(max)
declare #tableName varchar(100), #ColumnName varchar(50)
set #tableName = 'Employee'
set #ColumnName = 'ID'
select #x = DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
where
Table_Name = #tableName
and column_name = #ColumnName
select #x
All user-defined variables in T-SQL have private local-scope only. They cannot be seen by any other execution context, not even nested ones (unlike #temp tables, which can be seen by nested scopes). Using "##" to try to trick it into making a global-variable doesn't work.
If you want to execute dynamic SQL and return information there are several ways to do it:
Use sp_ExecuteSQL and make one of the parameters an OUTPUT parameter (recommended for single values).
Make a #Temp table before calling the dynamic SQL and then have the Dynamic SQL write to the same #Temp table (recommended for multiple values/rows).
Use the INSERT..EXEC statement to execute your dynamic SQL which returns its information as the output of a SELECT statement. If the INSERT table has the same format as the dynamic SQL's SELECT output, then the data output will be inserted into your table.
If you want to return only an integer value, you can do this through the RETURN statement in dynamic SQL, and receive it via #val = EXEC('...').
Use the Session context-info buffer (not recommended).
However, as others have pointed out, you shouldn't actually need dynamic SQL for what you are showing us here. You can do just this with:
SET #x = ( SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE Table_name = #TabName
AND column_name = #colName )
You may want to consider using the sp_executesql stored procedure for dynamic sql.
The following link provides a good usage example of sp_executesql procedure with output parameters:
http://support.microsoft.com/kb/262499