Query with null-terminated strings - sql

I'm trying to write a T-SQL script to align two copies of a database with the
tablediff utility exposed by SQL Server but I am facing a problem with all the rows of a text field inside a table.
The problem is that all the strings stored on that text field are null-terminated (there actually is a null character at the end of the string if I export them to a text file) and the INSERT and UPDATE queries generated by tablediff are all failing due to the fact that that null terminator truncates the query.
Is there a way to prevent the generated scripts from failing?
UPDATE
Creation query generated by MSSQL for the table I'm trying to align
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[TABTEST](
[Code] [varchar](50) NOT NULL,
[Source] [text] NULL,
CONSTRAINT [PrK_TABTEST] PRIMARY KEY CLUSTERED
(
[Code] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
Insert query generated by tablediff (keep in mind that the character you don't see at the end of the Source after the NOOP is an ANSI NULL character)
UPDATE [dbo].[TABTEST] SET [Source]=N'NOOP ' WHERE [Code] = N'TestCode'

The CHAR(0) can be nasty...
My suggestion is to cut the last character away, LEN() will return the lengt including this 0x0. Try it out:
DECLARE #s VARCHAR(100)='test' + CHAR(0)
SELECT #s + 'extension' AS Result1,
LEFT(#s,LEN(#s)-1) + 'extension' AS Result2;

Related

SQL DLETE Statement not working

I have a customized application in my company where I can create a place for users to input their values to a database.
The table where I am submitting the data has 5 columns with its SQL CREATE Query as below:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Log_Ongoing](
[ID] [int] IDENTITY(1,1) NOT NULL,
[LogType] [int] NULL,
[ActivityDate] [datetime] NOT NULL,
[ActivityDescription] [text] NULL,
[Train] [int] NULL,
CONSTRAINT [PK_Log_Ongoing] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Log_Ongoing] WITH CHECK ADD CONSTRAINT [FK_Log_Ongoing_Trains] FOREIGN KEY([Train])
REFERENCES [dbo].[Trains] ([Id])
GO
ALTER TABLE [dbo].[Log_Ongoing] CHECK CONSTRAINT [FK_Log_Ongoing_Trains]
GO
The purpose of this table is to record the ongoing activities in the plant.
The user can come later and modify those activities by updating, adding or deleting through the application by choosing the report data then modifying the data.
My thinking was that before the user submits the data I will delete the old data with the same report date first then insert the new data again.
Unfortunately the data is submitted successfully, but not deleted.
I made a SQL trace to check the queries that the application sends to the database, and I found the below two statements:
exec sp_executesql N'DELETE FROM Log_Ongoing WHERE ActivityDate = #StartDate',N'#startDate datetimeoffset(7)',#startDate='2017-02-12 07:00:00 +02:00'
exec sp_executesql N'INSERT INTO Log_Ongoing (LogType, ActivityDate, ActivityDescription, Train ) VALUES (1,#StartDate, #Activity, #Train)',N'#Train int,#Activity nvarchar(2),#startDate datetimeoffset(7)',#Train=1,#Activity=N'11',#startDate='2017-02-12 07:00:00 +02:00'
When I tested the INSERT staement in the SSMS, it worked fine, but then when I tested the DELETE statement, it didn't work. What is wrong with this query?

Filtered Unique Index causing UPDATE to fail because incorrect 'QUOTED_IDENTIFIER' settings

We've put in place the following filtered index on a table in our SQL Server 2016 database:
CREATE UNIQUE NONCLUSTERED INDEX [fix_SystemPKeyExecutionOrder] ON [DataInt].[TaskMaster]
(
[SystemPkey] ASC,
[ExecutionOrder] ASC
)
WHERE ([ExecutionOrder] IS NOT NULL)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 95)
GO
Which is causing SQL code to fail now with the following error:
UPDATE failed because the following SET options have incorrect
settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for
use with indexed views and/or indexes on computed columns and/or
filtered indexes and/or query notifications and/or XML data type
methods and/or spatial index operations. [SQLSTATE 42000] (Error
1934). The step failed.
When the filtered index is removed, the code runs perfectly.
Looking on MSDN for Index Options, there's nothing about QUOTED_IDENTIFIERS.
None of the UPDATE statements in our SQL code have double quotes for any of the values. The only double-quotes we can see are the following:
SET #ROWCOUNT = ##ROWCOUNT
If (#ROWCOUNT = 0)
BEGIN
RAISERROR('The "File Import" task ACTIVE_YN could not be updated to "Y". Either the task does not exist or the system "File Import To Stage" does not exist.', 16, 1)
END
ELSE
BEGIN
Print 'Successfully updated the "File Import" task ACTIVE_YN to "Y".'
END
Even if we change those double quotes " to two single quotes '', the code still fails with the same error.
The table itself was created with:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [DataInt].[TaskMaster](
[Pkey] [bigint] IDENTITY(1,1) NOT NULL,
[ScheduleMasterPkey] [int] NOT NULL,
[SystemPkey] [int] NOT NULL,
[SourcePkey] [int] NOT NULL,
[TargetPkey] [int] NOT NULL,
[TaskName] [varchar](255) NOT NULL,
[TaskTypePkey] [int] NOT NULL,
[Active_YN] [char](1) NOT NULL,
[ModifiedDate] [datetime] NULL,
[ModifiedBy] [varchar](100) NULL,
[RowVersion] [timestamp] NOT NULL,
[ExecutionOrder] [int] NULL,
CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED
(
[Pkey] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 95) ON [PRIMARY],
CONSTRAINT [uc_TaskName] UNIQUE NONCLUSTERED
(
[TaskName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 95) ON [PRIMARY]
) ON [PRIMARY]
GO
Like I said though, the entirety of the code runs perfectly if we do not create the filtered index; it only fails with the index.
So why is the filtered index suddenly causing our SQL to bomb and how can we fix it?
UPDATE: here is a small snippet of code that reproduces the failure. This code is run through an SQL Agent Job. When the index is removed, this code runs as expected stating the error the task does not exist:
DECLARE #ROWCOUNT INT = 0
UPDATE [DataIntegrationMaster].[DataInt].[TaskMaster]
Set Active_YN = 'Y'
where TaskName = 'File Import'
and SystemPkey = 0
SET #ROWCOUNT = ##ROWCOUNT
If (#ROWCOUNT = 0)
BEGIN
RAISERROR('The "File Import" task ACTIVE_YN could not be updated to "Y". Either the task does not exist or the system "File Import To Stage" does not exist.', 16, 1)
END
ELSE
BEGIN
Print 'Successfully updated the "File Import" task ACTIVE_YN to "Y".'
END
UPDATE2 with ANSWER:
As pointed out by the helpful answers below, I had to put
SET QUOTED_IDENTIFIER ON
at the top of the SQL for it to work properly.
SET QUOTED_IDENTIFIER ON
has NO EFFECT when I use it creating the index.
There is: SET QUOTED_IDENTIFIER (Transact-SQL)
In order to prevent similar issues, I would recommend to check the exact requirements for creating a filtered index: CREATE INDEX (Transact-SQL). It has a nice neat table that shows SET options required for a filtered index to be created.
As pointed out in #Roger Wolf's answer, creating a filtered index requires you to have the QUOTED_IDENTIFER setting to be set to ON, which is what you did. Had you not done so, you would have been unable to create the filtered index in the first place.
However, once created, it would seem that any DML operation (not just updates) on that table require you to have the QUOTED_IDENTIFER setting to be set to ON as well. This is what you are currently missing, and the reason why you get the error.
So, I don't know what the context of your update is, whether you are running this as an ad-hoc statement, or if this is part of a stored procedure. Either way, make sure to include the SET QUOTED_IDENTIFIER ON statement somewhere at the beginning.

Update / Insert records to a table with a MERGE Statement

I did this 3 years ago with DB2 but can't remember how.
All I want to do is Update/Insert a record into a table. Rather than test for its existence and changing my DML, I want to do this with a parameterized insert/update(merge) T-SQL statement. I believe the procedure compiler optimizer will make this the most efficient method.
USE [MY_DB]
GO
/****** Object: Table [dbo].[map_locations] Script Date: 10/11/2015 9:29:26 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[map_locations](
[loc_min_lat] [varchar](5) NOT NULL,
[loc_min_lng] [varchar](6) NOT NULL,
[loc_id] [int] NULL,
[center] [varchar](20) NOT NULL CONSTRAINT [DF__map_locs__call___5E4ADDA8] DEFAULT (''),
CONSTRAINT [PK__map_map_locs__79C80F94] PRIMARY KEY CLUSTERED
(
[map_locations_lat] ASC,
[map_locations_lng] ASC,
[center] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
Simply put, I want to be able to insert a record into the above table if it does not violate the PK but update it if the record (from a PK perspective) exists.
I have been researching and all the MS SQL examples are for two table merges. I am passing in a record via parameters.
I am working in Delphi XE10 (not that that should matter) and the database is MS SQL 2012.
Any help appreciate.
When you say "rather than", I assume you're using Sql Server 2008 or later and are wanting to use its Merge command (apologies for the earlier version of this answer).
Here's a sample TransactSql script (tested and working, but using my table structure) which assumes you know the PK in advance:
declare #id int
select #id = 1
merge table1 as dest
using (values (#id, 'name1'))
as source (id, name)
on dest.id = #id
when matched then
update
set name = source.name
when not matched then
insert ( id, name)
values ( source.id, source.name);
select * from table1
From a Delphi app, you'd want to write that as a parameterized query, or, better, a parameterized call to a stored proc on the server.
These days, no Delphi-tagged q about Sql seems complete without a mention of Sql-Injection, but using a parameterized query should minimise the risk of that.

Unique constraint for varbinary column

I have a varbinary column in SQL, and I want this column to be always unique. However, I see that SQL doesn't allow to create a unique constraint on a varbinary column.
Is there any workaround to ensure this uniqueness? Maybe by using some other type of constraint, or something else?
Thanks!
If the varbinary is reasonably short then you could create a computed column of the hex representation and put a unique constraint on that. Ref SQL Server converting varbinary to string for how to convert varbinary to hex string.
Edit1: As pointed out by #GiorgiNakeuri the limit for unique constraints is 900 bytes, so 450 bytes in hex.
Edit2: Alternatively, if you can tollerate a (very) small risk of failure then you could create a computed column with an MD5 hash of the varbinary value and put the unique constraint on that. See the docs for HASHBYTES.
I guess you have VARBINARY(MAX). The length of it is 2^31-1 bytes, but the maximum length of key should be 900 bytes. So you are actually limited with VARBINARY(900). If the size of VARBINARY <= 900, you can add unique index.
As a workaround you can add Trigger and rollback inserts, if there is already same values in table.
A simpler solution could be to manually maintain the uniqueness of the column by checking the existence of the value to be inserted or updated before inserting or updating the column.
Example:
DECLARE #Exists BIT = (
SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END
FROM [Schema].[Table]
WHERE [DecryptedColumn] = #NewValueToCheck
)
IF (#Exists = 0)
BEGIN
-- Insert or Update
END
ELSE
-- Return Error
BEGIN
END
You could make the column a primary key. Scripted test table shown below
/****** Object: Table [dbo].[Table_1] Script Date: 02/03/2015 12:19:22 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Table_1](
[test] [varbinary](50) NOT NULL,
[rf] [nchar](10) NULL,
CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED
(
[test] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO

SQL Server - Identity column Guid

EDIT :
Well looks like the only permanent part is the last part, everytime a new session is opened the other parts also change.
Is there any way to control this ?
------------------------------------------------
I created an identity primary key column on SQL Server 2012.
I noticed that everytime a new record is created the only part that changes is the first part. The other parts marked as XXX below remain the same. Will it change in the future? Maybe after some millions of records?
(e.g 85BD420D-XXXX-XXXX-XXXX-XXXXXXXXXXXX)
The table was created using MVC Code First, however please find below the script generation
/****** Object: Table [dbo].[Transactions] Script Date: 17-01-2014 11:25:20 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Transactions](
[TransactionId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_dbo.Transactions] PRIMARY KEY CLUSTERED
(
[TransactionId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Thanks