We are going to migrate from SQL Server 2008 to 2016. I am in the process of identifying errors and I get the following error in a trigger:
Incorrect syntax near '51001'.
I looked through the Raiserror documentation on the Microsoft website and it did not help. The following is the SQL. Any help would be appreciated.
IF (SELECT count(*) FROM dbo.u_sample_concrete ref, deleted
WHERE ref.lab_cd = deleted.lab_id) > 0
BEGIN
RAISERROR 51001 ''Trigger td_tblLAB on table dbo.tblLAB: Primary key values found in table dbo.u_sample_concrete (lab_cd). Delete restricted; the transaction is being rolled back.''
ROLLBACK TRANSACTION
RETURN
END
Adding to #DaleK's answer with the proper syntax, the problem RAISERRROR syntax is long obsolete. IIRC, it was deprecated 20 years ago (with the SQL Server 2000 release) and removed entirely in SQL Server 2012.
Below is a powershell script that uses the T-SQL Script DOM (also available with the Dacfx NuGet package) to identify existing T-SQL modules with invalid syntax. It won't catch problems in dynamic SQL, though.
$connectionString = "Data Source=.;Initial Catalog=YourDatabase;Integrated Security=SSPI"
try {
$query = #"
SELECT
QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + N'.' + QUOTENAME(OBJECT_NAME(object_id)) AS ObjectName
, OBJECTPROPERTY(object_id, 'ExecIsQuotedIdentOn') AS ExecIsQuotedIdentOn
, definition
FROM sys.sql_modules;
"#
Add-Type -Path "C:\Program Files (x86)\Microsoft SQL Server\140\DAC\bin\Microsoft.SqlServer.TransactSql.ScriptDom.dll"
$connection = New-Object Data.SqlClient.SqlConnection($connectionString)
$command = New-Object Data.SqlClient.SqlCommand($query, $connection)
$connection.Open()
$reader = $command.ExecuteReader()
while ($reader.Read()) {
# use TSqlParser appropriate for your SQL Server version
$parser = New-Object Microsoft.SqlServer.TransactSql.ScriptDom.TSql130Parser($reader["ExecIsQuotedIdentOn"])
$parseErrors = New-Object Collections.Generic.List[Microsoft.SqlServer.TransactSql.ScriptDom.ParseError]
$scriptReader = New-Object IO.StringReader($($reader["definition"]))
Write-Host "Parsing $($reader["ObjectName"]) ..."
[void]$parser.Parse($scriptReader, [ref]$parseErrors)
if($parseErrors.Count -ne 0) {
Write-Host "Parsing errors for object $($reader["ObjectName"]): $($parseErrors | ConvertTo-Json)" -ForegroundColor Yellow
}
}
$connecton.Close()
}
catch {
throw
}
The Microsoft Documentation does indeed show 4 things wrong with your statement.
The parameters must be inside brackets raiserror().
4 parameters are expected when msd_id (51001) is used - you are providing 2.
Parameters should be separated by commas ,.
You are double quoting the string, when it should be single quoted.
RAISERROR (51001, -1,- 1, 'Trigger td_tblLAB on table dbo.tblLAB: Primary key values found in table dbo.u_sample_concrete (lab_cd). Delete restricted; the transaction is being rolled back.')
Note: its best practice now to use throw rather than raiserror.
Related
I am trying to run the following query, which takes someone's name and attempts to insert it into an SQL Server database table.
$name = "Ronnie O'Sullivan"
$dataSource = "127.0.0.1"
$database = "Danny"
$connectionString = "Server=$dataSource;Database=$database;Integrated Security=True;"
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$query = "INSERT INTO People(name) VALUES('$name')"
$command = $connection.CreateCommand()
$command.CommandText = $query
$command.ExecuteNonQuery()
$connection.Close()
The problem I am facing is that the single quote is causing an issue in my query. The query is being executed as
INSERT INTO People(name) VALUES('Ronnie O'Sullivan')
which causes an SQL syntax error.
My question is how do I escape my $name variable so that it renders on the SQL side.
One solution is to do a find and replace on my $name variable, find: ' replace: ''
$name.Replace("'", "''")
Is there a more elegant solution out there, or a function that I can't seem to find?
Thank you.
You can try to update your code to to use a parametrised value that will cope with quotes in a string:
$query = "INSERT INTO People(name) VALUES(#name)"
$command = $connection.CreateCommand()
$command.CommandText = $query
$command.Parameters.Add("#name", $name) -- | Out-Null (may be required on the end)
$command.ExecuteNonQuery()
I'm not experienced with powershell but referenced this post for a parametrised query:
Tanner's helpful answer is definitely the most robust and secure solution, because using a [parameterized / prepared statement (query) eliminates any possibility of a SQL injection attack.
However, in this constrained case, where you want to insert a value into a single-quoted SQL string ('...'), you can get away with simply doubling any embedded ' characters in the value:
$query = "INSERT INTO People(name) VALUES('$($name -replace "'", "''")')"
The above uses PowerShell's string interpolation via $(...), the subexpression operator, to embed an expression that uses the -replace operator to double all embedded ' instances in the value of $name.
Note: You could also use $name.Replace("'", "''") above, which performs better in this simple case, but PowerShell's -replace operator is generally preferable, not only for being PowerShell-native, but for offering superior abilities, because it is regex-based and supports array as its LHS - see this comment on GitHub.
I'm quite new when it comes to Powershell and I'm not sure if my method is the best.
How can I pass Powershell variables into SQL using Invoke SQL or the .Net method? All I'm trying to do is to Insert data from the Get-AzureADGroup Display Name and Object Id into a SQL table.
$Groups = Get-AzureADGroup -All $true
foreach($Group in $Groups){
$DN = $Group.DisplayName
$ID = $Group.ObjectId
$insertquery="
INSERT INTO [dbo].[Table]([DsiplayName],[ObjectId])
VALUES(''$DN'',''$ID'')
"
Invoke-Sqlcmd -ServerInstance "Server" -Database "Database" -Query $insertquery
}
However, it comes up with the error 'Incorrect syntax near 'GroupDisplayName'. I've tried using " and ' interchangeably in the query with no luck. Am I missing something?
What I'm trying here is, to load the data from .xml file to the SQL server database using PowerShell Script.
My Script goes like:
$dataSource = 'dtsrc';
$database = 'tempdb'; #DB
$connection = New-Object System.Data.SqlClient.SqlConnection; #setting connection
$connection.ConnectionString = "Server=$dataSource;Database=$database;Integrated Security=True;";
$connection.Open();
$command = New-Object System.Data.SqlClient.SqlCommand;
$command.Connection = $connection;
$as = New-Object System.Data.SqlClient.SqlDataAdapter;
$as.SelectCommand = $command;
$filename = 'C:\DCT\XML\apc.xml'; #file to be loaded into sql server database table
$ds = New-Object System.Data.DataSet;
$ds.ReadXml($filename); #reading from the file -- line gives output InferSchema
$dt = New-Object System.Data.DataTable;
$dt.Columns.Add("StartTime",[datetime]);
$dt.Columns.Add("EndTime",[datetime]);
$dt.Columns.Add("Status",[int]);
$dt.Columns.Add("ProcessedTime",[datetime]);
$dt.Columns.Add("ScheduleId",[guid]);
$dt.Columns.Add("Model",[string]);
$dt.Columns.Add("ModelVersion",[string]);
$dt.Columns.Add("ModelVersionState",[string]);
$dt.Columns.Add("ModifiedTime",[datetime]);
$dt.Columns.Add("WriteBackLastRunTime",[datetime]);
$dt.Columns.Add("ModifiedBy",[string]);
$dt.Columns.Add("ModelType",[int]);
$dt.Columns.Add("IsTimeRange",[int]);#b
$dt.Columns.Add("WriteBackStatus",[int]);
$dt.Columns.Add("RetryWriteBack",[int]);#b
$dt.Columns.Add("NumOfRetry",[int]);
$dt.Columns.Add("FrequencyUnits",[int]);
$dt.Columns.Add("ScheduleType",[int]);
$dt.Columns.Add("CustomType",[int]);
$dt.Columns.Add("ShiftStartTime",[datetime]);
$dt.Columns.Add("StartWeekDay",[int]);
$dt.Columns.Add("EndWeekDay",[int]);
$dt.Columns.Add("WritebackProcessedTime",[datetime]);
$dt.Columns.Add("DiagStatus",[int]);
$dt.Columns.Add("AccountingPeriodCase_PK_ID",[guid]);
$dt = $ds.Tables[0];
$bcp = New-Object 'Data.SqlClient.SqlBulkCopy' $connection; #bulkcopy to the destination table.
$bcp.DestinationTableName = 'dbo.tempor';
#$bcp.ColumnMappings.Count;
$bcp.ColumnMappings.Clear();
$bcp.ColumnMappings.Add('StartTime','StartTime');
$bcp.ColumnMappings.Add('EndTime','EndTime');
$bcp.ColumnMappings.Add('Status','Status');
$bcp.ColumnMappings.Add('ProcessedTime','ProcessedTime');
$bcp.ColumnMappings.Add('ScheduleId','ScheduleId');
$bcp.ColumnMappings.Add('Model','Model');
$bcp.ColumnMappings.Add('ModelVersion','ModelVersion');
$bcp.ColumnMappings.Add('ModelVersionState','ModelVersionState');
$bcp.ColumnMappings.Add('ModifiedTime','ModifiedTime');
$bcp.ColumnMappings.Add('WriteBackLastRunTime','WriteBackLastRunTime');
$bcp.ColumnMappings.Add('ModifiedBy','ModifiedBy');
$bcp.ColumnMappings.Add('ModelType','ModelType');
$bcp.ColumnMappings.Add('IsTimeRange','IsTimeRange');
$bcp.ColumnMappings.Add('WriteBackStatus','WriteBackStatus');
$bcp.ColumnMappings.Add('RetryWriteBack','RetryWriteBack');
$bcp.ColumnMappings.Add('NumOfRetry','NumOfRetry');
$bcp.ColumnMappings.Add('FrequencyUnits','FrequencyUnits');
$bcp.ColumnMappings.Add('ScheduleType','ScheduleType');
#$bcp.ColumnMappings.Add('CustomType','CustomType');
#$bcp.ColumnMappings.Add('ShiftStartTime','ShiftStartTime');
#$bcp.ColumnMappings.Add('StartWeekDay','StartWeekDay');
#$bcp.ColumnMappings.Add('EndWeekDay','EndWeekDay');
$bcp.ColumnMappings.Add('WritebackProcessedTime','WritebackProcessedTime');
$bcp.ColumnMappings.Add('DiagStatus','DiagStatus');
$bcp.ColumnMappings.Add('AccountingPeriodCase_PK_ID','AccountingPeriodCase_PK_ID');
if ($connection.State -ne [Data.ConnectionState]::Open) {
'Connection to DB is not open.'
Exit
}
$bcp.WriteToServer($dt); #writing to server
$connection.Close();
The error I'm facing is:
Exception calling "WriteToServer" with "1" argument(s): "The given value of type String from the data source cannot be converted to type uniqueidentifier of the specified target column." At C:\Documents\DCT\SavedScripts\XMLtoDB\AccountingPeriodCases\sample.ps1:91 char:1
+ $bcp.WriteToServer($dt); #writing to server
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : InvalidOperationException
and the table I created has the columns with datatypes:
The thing is, I guess i need to convert the String that is in the datatable under the column ScheduleId and AccountingPeriodCase_PK_ID as they are not getting converted to uniqueidentifier.
I tried using
[System.Guid]::Parse($dt.Columns[4]);
[System.Guid]::Parse($dt.Columns[24]);
but the error repeats.
The xml content to be loaded under thr ScheduleId and AccountingPeriodCase_PK_ID looks like:
<ScheduleId>db6f3178-4702-456c-904b-2cd833b54efa</ScheduleId>
<AccountingPeriodCase_PK_ID>9544940d-8d1a-e711-80d3-0050569c2fb6</AccountingPeriodCase_PK_ID>
Could anyone help me resolve this issue?
Thank you
From a scenario, which I had faced earlier, it comes down to the source of your input.
The thing is if you are reading from an XML file, then you are bound to get an error, because while reading from a flat-file(txt, csv) or an XML, PowerShell will resolve the undefined types to String. The trick is to have some sort of control over the source. If your source is a PowerShell variable such as datatable or an array, then you can directly insert values from this variable to your destination table, since the original data types of the columns in a table are preserved.
This is the approach which I followed. In my case, the source was a result of the Invoke-Sqlcmd which preserved the original data types and hence made insertion error less.
Again as mentioned in the comment by OP, he was parsing values based on columns and not rows. This could also lead to an error. For example, there is one more way of inserting values in a table which is using the INSERT statement. Mind you that, the SQL Server INSERT statement inserts value on a row-level and not a column level. In such a scenario, parsing the values column wise may fail.
I use a T-SQL command which I found here to get the fragmentation of my database tables. When I execute the T-SQL in the Management Studio, everything works. If I use it inside PowerShell, I get the following error (translated from German):
Exception when calling "ExecuteReader" with 0 Argument(s):
"Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'."
In Zeile:17 Zeichen:6
+ $Result = $cmd.ExecuteReader()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : SqlException
This is the part of my script which throws the error
foreach ($table in $tables)
{
$Data = New-Object System.Data.DataTable
$cmd = New-Object System.Data.SqlClient.SqlCommand
$getFragRate = "
-- SQL Command von Microsoft um die Index Fragmentation zu überprüfen
USE Logik;
GO
SELECT a.index_id, name, avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats (DB_ID(N'Logik'), OBJECT_ID(N'$($table)'), NULL, NULL, NULL) AS a
JOIN sys.indexes AS b ON a.object_id = b.object_id AND a.index_id = b.index_id;
GO
"
$cmd.CommandText = $getFragRate
$cmd.Connection = $con
$Result = $cmd.ExecuteReader()
$Data.Load($Result)
$Data
}
Why does this error occur?
The User I use to do this has sysadmin, db_ddladmin and db_owner permission.
Edit: Another T-SQL Command to get all tables of my database worked without a problem from PowerShell.
GO is NOT a T-SQL command - therefore you cannot have it in T-SQL statements being executed from PowerShell.
GO is a batch separator used by SQL Server Management Studio.
You need to break up that statement into several individual statements yourself and execute them one by one.
I'm trying to call a maintenance SP from within an Azure runbook:
inlinescript {
.........
$Cmd = New-object System.Data.SqlClient.SqlCommand
$Cmd.Connection = $Conn
$Cmd.CommandText = "EXEC [dbo].[BackupLogTable] #tableName, #olderThan"
$Cmd.Parameters.AddWithValue("#tableName", $TableName)
$Cmd.Parameters.AddWithValue("#olderThan", $OlderThan)
$Cmd.ExecuteNonQuery()
.....
}
The SP is declared like this:
alter procedure [dbo].[BackupLogTable] (
#tableName nvarchar(512),
#olderThan int
)
with execute as owner as
and I can successfully run it from SSMS under the same user my runbook uses. But when testing it in Azure portal I'm getting the following error:
Exception calling "ExecuteNonQuery" with "0" argument(s): "The
parameterized query '(#tableName nvarchar(4000),#olderThan
nvarchar(4000))EXEC [dbo].' expects the parameter '#tableName', which
was not supplied."
I tried every other variants of passing the parameters found on the net like this one:
$Cmd.CommandText = "[BackupLogTable]"
$Cmd.CommandType = [System.Data.CommandType]::StoredProcedure
$Cmd.Parameters.Add("#tableName", [System.Data.SqlDbType]::NVarChar, 512) | Out-Null
$Cmd.Parameters["#tableName"].Value = $TableName
$Cmd.Parameters.Add("#olderThan", [System.Data.SqlDbType]::Int) | Out-Null
$Cmd.Parameters["#olderThan"].Value = $OlderThan
and many others but it always fails:
Exception calling "ExecuteNonQuery" with "0" argument(s): "Procedure
or function 'BackupLogTable' expects parameter '#tableName', which
was not supplied."
What am I doing wrong?
How are you passing your parameters to your inline script?
There's some limitations; e.g. Get-AutomationVariable / AutomationCredential is not available in the InlineScript.
"The InlineScript activity runs a block of commands in a separate, non-workflow session and returns its output to the workflow. While commands in a workflow are sent to Windows Workflow Foundation for processing, commands in an InlineScript block are processed by Windows PowerShell. The activity uses the standard workflow common parameters including PSComputerName and PSCredential which allow you to specify that the code block be run on another computer or using alternate credentials."
So, there's some limitations on how to pass and get variables.
However you can pass values into the inlinescript using $Using.
E.g: InlineScript { Write-output $using:TableName }
Hopefully that'd do the trick.
See also the recommendations for inlinescript on: https://technet.microsoft.com/en-us/library/dn469257(v=sc.16).aspx#bkmk_InlineScript