Why would an Insert statement always fire no matter the results of the preceeding IF statement? - sql-server

I am using a SQL Server 2005 database, and I have an If statement that is behaving in two different manners against the same data set, depending on the actions to be taken in the begin...end block.
First, if I merely want to print to the console, the following code prints nothing, as expected:
if (not exists(select null from tblControls where Name = 'SOME_CONTROL_NAME'))
begin
print 'control not found'
end
Whereas this code prints 'control found' as expected:
if (exists(select null from tblControls where Name = 'SOME_CONTROL_NAME'))
begin
print 'control found'
end
However, if I change the code to this:
if (not exists(select null from tblControls where Name = 'SOME_CONTROL_NAME'))
begin
insert into tblControls values (632, 'NEW_CONTROL_NAME', 'New Control', 1, 1, NULL, 1, 'DataControls.CheckBox', NULL, NULL, 1)
end
The insert statement ALWAYS fires, even though a matching record exists in tblControls. Is there something special about insert statements in T-SQL 2005 that could be causing this behavior, or am I missing something obvious? I would understand if the logic of the code is wrong, but when I tested using the print statement it works as expected.
EDIT: Its generating an "Insert Error" message.
Any help with this frustrating problem is greatly appreciated.

It's the NULL in the Exists..Select that's confusing it. Try:
if (not exists(select * from tblControls where Name = 'SOME_CONTROL_NAME'))
begin
insert into tblControls values (632, 'NEW_CONTROL_NAME', 'New Control', 1, 1, NULL, 1, 'DataControls.CheckBox', NULL, NULL, 1)
end
Also, it can be rewritten without the parentheses and Begin/End:
IF NOT EXISTS (SELECT * FROM tblControls WHERE Name = 'SOME_CONTROL_NAME')
INSERT INTO tblControls VALUES (632, 'NEW_CONTROL_NAME', 'New Control', 1, 1, NULL, 1, 'DataControls.CheckBox', NULL, NULL, 1)

Related

How to check IS NULL in dynamic query in sql server

I did following store procedure using dynamic query, see the following blueprint of code
ALTER PROCEDURE [dbo].[usp_Report] (
#LocationId INT = NULL
,#UserId INT = NULL)
DECLARE #miscquery NVARCHAR (MAX);
begin
SET #miscquery='
SELECT
,A.AgreementNumber AS Contract
,A.AgreementId
FROM tblAgreement A
WHERE
AND (A.IsDeleted = 0 or A.IsDeleted is null)
AND (
(
' + convert(NVARCHAR(30), #LocationId) + ' IS NULL
AND (
A.CheckoutLocation IN (
SELECT LocationId
FROM [dbo].[tblUserLocations]
WHERE UserID = ' + convert(VARCHAR(10), #userId) +'
AND IsDeleted = 0
)
OR A.CheckoutLocation IS NULL
)
)
OR A.CheckoutLocation = ' + convert(VARCHAR(10), #LocationId) +'
)'
EXEC (#miscquery)
end
)
here when i execute the query with #Locationid is null, results doesn't return table, it returns like following
(63 rows affected)
(2325 rows affected)
please help me. thank you
The code you have there cannot be your actual code, firstly because right at the start you try to set a variable called #miscquery before you declare it. There's also no reason for this code to be dynamic, so it's clear you're doing some other stuff as well.
I will take it as a given that for some reason you "need" dynamic SQL. I will put in the standard warning about sanitising your inputs. That was it.
OK. Even if #miscquery had been declared, the code as written will not produce any results. It will either throw a syntax error, or do nothing, depending on your setting for concat_null_yields_null.
Let's take the likely case: you have the default setting for this, which means that when you concatenate a varchar to null, the result is null.
Observe the following code:
declare #locationId int = null;
select 'this sentence ends with...' + convert(nvarchar(30), #locationId);
What will be the output?
"This sentence ends with... null"
"This sentence ends with..."
null
The answer is 3. And notice: that's not a string with the value "null". That's just null. When you convert the null value to a string, you don't get a string with the value "null", you get the null value.
OK, so now we try to add null to the end of our string. When you try to concatenate the null value to a string with the + operator, the entire result is null.
Therefore, your entire #miscquery variable is null at the end of the assignment.
So what you are then doing is the same as this:
declare #myquery varchar(max) = null;
exec sp_executesql #myquery
Which is valid, and doesn't throw any error, but does nothing. Then the rest of your code does whatever it does, and produces the results you see.
if concat_null_yields_null was set to off then you would get a syntax error, because the resulting text would not be valid SQL when you handed it to sp_executesql.
As Dan said, the best and safest solution would be to parameterize your inputs. But putting that aside, the solution you need would look something like the following. In the iif() function, I look at the value of #locationId. If it is null, then I want the string "null". If it is not null, then I want the string conversion of whatever value it has.
declare #locationId int = null;
declare #query varchar(max) =
'select * from mytable where '
+ iif(#locationId is null, 'null', convert(nvarchar(30), #locationId))
+ ' is null';
print #query;

Alter ADD tempDB inside SELECT?

How can I alter tempdb inside a select?
I want a single query is that possible?
SELECT cust_ac_no, ord_no, ref_no, net_svc_id, job_type, ord_status, ord_status_date, ord_crt_date
INTO tempdb..xtiankwiksetreport
ALTER table tempdb..xtiankwiksetreport
ADD serial_no varchar(25) null,
msisdn varchar(25) null,
imsi varchar(25) null,
bts_id varchar(25) null
FROM wo_order
WHERE job_type IN ('EXTR', 'EXTC')
AND svc_type='4G'
AND ref_no=2
AND ord_status IN ('PL', 'JL')
SELECT cust_ac_no, ord_no, ref_no, net_svc_id, job_type, ord_status, ord_status_date, ord_crt_date,
serial_no=convert(varchar(25), null), msisdn-convert(varchar(25), null), imsi=convert(varchar(25), null), bts_id=convert(varchar(25), null)
INTO tempdb..xtiankwiksetreport
FROM wo_order
WHERE job_type IN ('EXTR', 'EXTC')
AND svc_type='4G'
AND ref_no=2
AND ord_status IN ('JL', 'PL')
I have started to test this in Sybase before you added the sql-server tag so I will provide both, the first one explain the idea, the second will prove this is possible in both DBMS.
Note that the solution is working but I would not use this as a long term solution. I didn't research on the possibilities of this to be a documented behavior, and could fail in the future (or already, based on my database version)
Sybase
Just use a convert function on a null value to add the empty columns.
select 'a' as foo,
convert(varchar(25), null) as serial_no
into #temp
Then to test it, I insert a long value :
insert into #temp values ('b', 'abcdefghijklmnopqrstuvwxyz')
And the result :
select * from #temp
foo,serial_no
'a',
'b','abcdefghijklmnopqrstuvwxy'
The 26th character is missing, we have a varchar(25)
SQL-Server
The same code will provide an error due to the truncated value
select 'a' as foo, convert(varchar(25), null) as serial_no
into temp_foo
insert into temp_foo values ('b', 'abcdefghijklmnopqrstuvwxyz')
insert into temp_foo values ('c', 'abcdefghijklmnopqrstuvwxy')
select * from temp_foo
The line with b provide a value to long and gives me :
Msg 8152, Level 16, State 14, Line 4
String or binary data would be truncated.
The statement has been terminated.
But the line with c with 25 character fits.
foo,serial_no
'a',
'c','abcdefghijklmnopqrstuvwxy'
SELECT
cust_ac_no, ord_no, ref_no, net_svc_id, job_type, ord_status, ord_status_date, ord_crt_date,
convert(varchar(25), null) as serial_no,
convert(varchar(25), null) as msisdn,
convert(varchar(25), null) as imsi,
convert(varchar(25), null) as bts_id,
INTO tempdb..xtiankwiksetreport
FROM wo_order
WHERE job_type IN ('EXTR', 'EXTC')
AND svc_type='4G'
AND ref_no=2
AND ord_status IN ('PL', 'JL')

SQL Server DB Project Publish Empty string inserting a Zero

We are seeing a very strange problem when populating a field in a DB via a SQL Server DB project publish action.
This is the table definition
CREATE TABLE [dbo].[CatalogueItemExtensionFields]
(
[RowID] tinyint identity not null,
[FieldType] tinyint not null,
[Description] varchar(120) not null,
[Nullable] bit not null,
[DefaultValue] varchar(100) null,
[Active_Flag] bit null,
[OrderPriority] tinyint not null,
[ContextGuid] uniqueidentifier not null
);
This is the population script
set identity_insert CatalogueItemExtensionFields on
INSERT INTO CatalogueItemExtensionFields (rowid, fieldtype, description, nullable, defaultvalue, active_flag, orderpriority)
VALUES (dbo.ConstantProductGroupRowId(), 3, 'Product Group', 0, '', 1, dbo.ConstantProductGroupRowId()),
set identity_insert CatalogueItemExtensionFields off
If I run the INSERT script manually all works fine. When I run it as part of the DB project publish, it inserts "0".
I have looked at the publish.sql script that is generated, and all looks fine.
BTW, the only similar post I have found is this, but it does not apply to our case because the field we are inserting into is defined as varchar.
This is driving us mad. Any ideas?
We at our company finally found out that if you use SQLCMD / DBProj then it is super import to ENABLE Quoted Identifiers. Or else installer changes inputs in the exactly same way as #maurocam explained. If you enable this, then it works same as in SQL Management Studio for example.
To enable it:
SQLCMD
just use parameter -I (capital is important here, small is for file).
Example sqlcmd -S localhost -d DBNAME -U User -P Password -i path/to/sql/file.sql -I
SQL Itself
SET QUOTED_IDENTIFIER { ON | OFF }
DBProj
can be set at the project or object (proc, func, ...) level. Just click on a proj/file -> Properties and check if QUOTED_IDENTIFIER is enabled there.
For schema compare it can be set via "Ignore quoted identifiers"
https://learn.microsoft.com/en-us/sql/t-sql/statements/set-quoted-identifier-transact-sql?view=sql-server-ver16
APOLOGIES - MY MISTAKE!!! (But a very useful one to document)
I have summarised again below (also to make it clearer respect to my initial post)
TABLE DEFINITION
CREATE TABLE [dbo].[CatalogueItemExtensionFields]
(
[RowID] tinyint identity not null,
[FieldType] tinyint not null,
[Description] varchar(120) not null,
[Nullable] bit not null,
[DefaultValue] varchar(100) null,
[Active_Flag] bit null,
[OrderPriority] tinyint not null
);
INSERT STATEMENT
set identity_insert CatalogueItemExtensionFields on
INSERT INTO CatalogueItemExtensionFields (rowid, fieldtype, description, nullable, defaultvalue, active_flag, orderpriority) VALUES
(6, 3, N'Product Group', 0, N'', 1, 6),
(7, 2, N'Minimum Order Quantity', 1, NULL, 1, 7),
(8, 3, N'Additional HIBCs', 0, 1, 1, 8),
(9, 3, N'Additional GTINs', 0, N'', 1, 9)
set identity_insert CatalogueItemExtensionFields off
Because I am inserting multiple rows, when SQL parses the statement it see I am trying to insert a numeric defaultvalue = 1 for RowID = 8. As a result, even though the column is defined as a varchar, SQL decides that the INSERT statement is inserting INTs. So the empty string values (for RowIDs 7 and 9) are converted to zero. I referred to a post I had found relating to actual INT column, which results in the same behaviour.
If I instead run the following statement, with a default value of '1' for RowID = 8, it all works fine.
INSERT INTO CatalogueItemExtensionFields (rowid, fieldtype, description, nullable, defaultvalue, active_flag, orderpriority) VALUES
(6, 3, N'Product Group', 0, N'', 1, 6),
(7, 2, N'Minimum Order Quantity', 1, NULL, 1, 7),
(8, 3, N'Additional HIBCs', 0, '1', 1, 8),
(9, 3, N'Additional GTINs', 0, N'', 1, 9)
So, now the question is, why does SQL server ignore the column type definition and instead decides the type from the value in my INSERT statement?
Answer from SqlServerCentral
"empty string converts to an int without an error, and it's value is zero. it has to do with the precidence of implicit conversions"

mssql generating unique code

please help me in this,how would i do some recursive statement in my stored procedure.
here is what i want
-- #requestcode will genereate some random string i have already the code below
set #requestcode = (SELECT substring(#username,0,3)+'-'+SUBSTRING(CONVERT(varchar(255), NEWID()), 0, 9))
-- then i want to check if the string generated is existing to 'sampletable'
select #requestcode from sampletable
-- if it is existing back to the #requestcode query until it is not existing
thanks in advance
#requestcode starts as NULL (unless assigned already) so the first WHILE condition check is always true which gives at least one iteration
WHILE #requestcode IS NULL OR
EXISTS (SELECT * FROM sampletable WHERE requestcode = #requestcode)
BEGIN
SELECT #requestcode = substring(#username,0,3) + '-' +
SUBSTRING(CONVERT(varchar(255), NEWID()), 0, 9));
END

Getting the error "Invalid length parameter passed to the LEFT or SUBSTRING function."

I am trying to extract the login success and failures out of our SQL log file. This is the statement that I was able to get to work, and now I am getting an error when running it. Any help would be greatly appreciated. I know it is in the last statement because the rest works withouth error when running independently. Nothing has changed in the system so I don't understand why it would start giving the error?
CREATE TABLE [dbo].[#TmpErrorLog]
([LogDate] DATETIME NULL,
[ProcessInfo] VARCHAR(20) NULL,
[Text] VARCHAR(MAX) NULL);
CREATE TABLE [dbo].[#TmpErrorLog2]
([LogDate] DATETIME NULL,
[ProcessInfo] VARCHAR(20) NULL,
[Text] VARCHAR(MAX) NULL,
[LoginAttempt] VARCHAR(20) NULL);
INSERT INTO #TmpErrorLog ([LogDate], [ProcessInfo], [Text])
EXEC [master].[dbo].[xp_readerrorlog] 0 ;
INSERT INTO #TmpErrorLog2 ([LogDate], [ProcessInfo], [Text], [LoginAttempt])
Select LogDate, ProcessInfo, Replace(Text,'''','"') as Text, SUBSTRING(Text,0,16) as LoginAttempt
From #TmpErrorLog
Where LogDate > GETDATE() - 1 and Text like '%\%'
INSERT INTO LogData ([LogDate], [LoginAttempt], [LoginUser])
Select
LogDate,
Case LoginAttempt When 'Login succeeded' Then 'Successfull' Else 'Failed' End as LoginAttempt,
SUBSTRING(SUBSTRING(Text, CHARINDEX('"', Text,1), CHARINDEX('"', Text, CHARINDEX('"', Text, 0)) - 4),2,50) as LoginUser
From #TmpErrorLog2
Where LogDate Not In(Select LogDate From LogData)
Drop Table #TmpErrorLog
Drop Table #TmpErrorLog2
IIRC, CHARINDEX can return -1, which would not be a valid parameter to SUBSTRING. I'm guessing you are getting some non-hits on your CHARINDEXes.

Resources