I'm having a problem with a GUID which I've narrowed down to what looks like a problem with the LIKE operator.
When the following is run on my server
DECLARE #Problem NVARCHAR(1000) = N'58C21BC6-081B-4E57-BFE1-5B11AAC662F1';
DECLARE #GuidPattern NVARCHAR(1000) =
REPLICATE('[0-9A-Fa-f]', 8)
+ '-'
+ REPLICATE('[0-9A-Fa-f]', 4)
+ '-'
+ REPLICATE('[0-9A-Fa-f]', 4)
+ '-'
+ REPLICATE('[0-9A-Fa-f]', 4)
+ '-'
+ REPLICATE('[0-9A-Fa-f]', 12);
SELECT
CASE
WHEN #Problem LIKE #GuidPattern THEN 1
ELSE 0
END AS [FollowsPattern];
the answer is 0, but when I run the exact same code on my local machine the answer is 1.
It looks pretty obvious to me that the string is in fact a valid GUID, so the answer should be 1 in both cases. All the other ways I know about to confirm that the GUID is valid also succeed, both locally and on the server:
SELECT
CASE
WHEN CAST(#Problem AS UNIQUEIDENTIFIER) = #Problem THEN 1
ELSE 0
END AS [IsGuid1]; -- 1
SELECT
CASE
WHEN CONVERT(UNIQUEIDENTIFIER, #Problem) = #Problem THEN 1
ELSE 0
END AS [IsGuid2]; -- 1
SELECT
CASE
WHEN TRY_CONVERT(UNIQUEIDENTIFIER, #Problem) IS NOT NULL THEN 1
ELSE 0
END AS [IsGuid3]; -- 1
The server version is
Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (X64)
Apr 29 2016 23:23:58
Copyright (c) Microsoft Corporation
Enterprise Edition: Core-based Licensing (64-bit) on Windows Server 2012 R2 Standard 6.3 <X64> (Build 9600: ) (Hypervisor)
and my local installation version is
Microsoft SQL Server 2014 - 12.0.2000.8 (X64)
Feb 20 2014 20:04:26
Copyright (c) Microsoft Corporation
Express Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
The default collation is the not the same (SQL_Latin1_General_CP1_CI_AS locally and Danish_Norwegian_CI_AS on the server), but I don't think that should matter since I'm dealing with Unicode anyway. Adding an extra COLLATE clause on either machine with the other's collation makes no difference. Update: Not true, the collation is the source of the problem. I only tested different collations in the variable declaration, not in the comparison itself.
I suppose the problem is COLLATION.
Have a look at: http://rextester.com/IRQEFU33639
If you use default collation:
SELECT
CASE
WHEN #Problem LIKE #GuidPattern THEN 1
ELSE 0
END AS [FollowsPattern];
The result = 1
Instead, if you forces Danish_Norwegian_CI_AS collation:
SELECT
CASE
WHEN #Problem collate Danish_Norwegian_CI_AS LIKE #GuidPattern THEN 1
ELSE 0
END AS [FollowsPattern];
It returns 0.
I'd suggest to force the collation to SQL_Latin1_General_CP1_CI_AS or other collation that works well.
SELECT
CASE
WHEN #Problem COLLATE SQL_Latin1_General_CP1_CI_AS LIKE #GuidPattern THEN 1
ELSE 0
END AS [FollowsPattern];
And just to make it easy maybe using an INLINE User Defined Function.
Force to a standard collation
SELECT
CASE
WHEN #Problem COLLATE Latin1_General_CI_AS LIKE #GuidPattern COLLATE Latin1_General_CI_AS THEN 1
ELSE 0
END AS [FollowsPattern];
The reason is how "a" is matched in Danish_Norwegian_CI_AS collation.
See SQL 'Like' operator and 'aa' for more where I demonstrated it with Danish_Norwegian_CI_AS
Related
The following code works in my local instance of Sql Server but fails in remote instance with error Can you please help me out on this. Getting Error#
102: Incorrect syntax near '$.Location'
If (len(#JsonBODetails) > 0)
Begin
Insert Into #Temp_BOLines
SELECT * FROM
OPENJSON ( #JsonBODetails )
WITH (
Location varchar(2) '$.Location' ,
JCA varchar(4) '$.JCA'
)
End
Edit: SQL Server version. It is the same in both the cases.
Microsoft SQL Server 2017 (RTM-CU13-OD) (KB4483666) - 14.0.3049.1 (X64)
Dec 15 2018 11:16:42
Copyright (C) 2017 Microsoft Corporation
Web Edition (64-bit) on Windows Server 2016 Datacenter 10.0 (Build 14393: ) (Hypervisor)
Adding the complete procedure to repro this error
declare #JsonBODetails varchar(max)
SELECT * FROM
OPENJSON ( #JsonBODetails )
WITH (
Location varchar(2) '$.Location' ,
JCA varchar(4) '$.JCA'
)
Interestingly enough I found no post for this specific, but basic issue.
Goal: update the latest budgetid record docstatus = 0. Then I want to update the next-to-last budgetid record docstatus = 1. I am trying this within PHP but also testing in my SQL Server SEM and it is failing there, too.
My SQL Server statement:
select
budgetid, docstatus, datechanged
from
ccy_budget
where
activityid = 11111
order by
datechanged desc
limit 1,1;
Error that occurs in SEM is:
Incorrect syntax near 'limit'.
Yet in w3schools this [sample] sql works just fine:
SELECT *
FROM Customers
ORDER BY postalcode DESC
LIMIT 1,1;
Seems so simple, surely I am missing something fundamental.
Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64)
Apr 2 2010 15:48:46
Copyright (c) Microsoft Corporation
Enterprise Edition (64-bit) on Windows NT 6.2 <X64> (Build 9200: ) (Hypervisor)
Equivalent syntax in SQL Server would be
select *
from table
order by somerow desc
offset 1 rows fetch next 1 rows only;
But the above is available from SQL Server 2012 on, so for your version, you have to some thing like below
;with cte
as
(
select *,row_number() over (order by postalcode desc) as rn
from table
)
select * from cte where rn=2
This is the simplest example with which I could reproduce the issue. As such it looks a little bit contrived, but bear with me.
declare #t table(number int)
insert into #t values (1), (2)
declare #sum bigint = 0
select #sum = #sum + number
from (select top 2 number from #t order by number) subquery
order by number desc
select #sum
Here's the query on the data explorer.
I would expect this to return 3, the sum of the values in the table #t. Instead, however, it returns 1.
Doing any of the following will cause the query to correctly return 3:
make #t.number and #sum have the same type (by making #sum an int or #t.number a bigint).
removing the outer order by
removing the inner order by
making both order bys sort in the same direction by adding desc to the inner one or removing it from the outer one
removing the subquery (i.e. just selecting from #t)
None of these things strike me as something that should change the behavior of this query.
Swapping the sort orders (descending in the subquery, ascending on the outside) will make the query return 2 instead of 1.
A similar thing happens with strings instead of numbers, so this isn't constrained to int and bigint.
This happens both with SQL Server 2014 and 2016, or to be precise
Microsoft SQL Server 2014 - 12.0.2000.8 (X64)
Feb 20 2014 20:04:26
Copyright (c) Microsoft Corporation
Developer Edition (64-bit) on Windows NT 6.3 <X64> (Build 10586: )
and
Microsoft SQL Server 2016 (RTM-CU1) (KB3164674) - 13.0.2149.0 (X64)
Jul 11 2016 22:05:22
Copyright (c) Microsoft Corporation
Enterprise Edition: Core-based Licensing (64-bit) on Windows Server 2012 R2 Standard 6.3 <X64> (Build 9600: )
(the latter being the data explorer).
What's going on?
The answer seems to be that you are/were relying on undocumented behaviour which changed in Sql Server 2012.
Per the documentation:
https://msdn.microsoft.com/en-us/library/ms187330.aspx
SELECT #local_variable is typically used to return a single value into the variable. However, when expression is the name of a column, it can return multiple values. If the SELECT statement returns more than one value, the variable is assigned the last value that is returned.
It is not documented what happens if the destination variable (to be assigned to) is part of the source expression. It seems this behaviour has changed. In earlier versions the variable would be assigned once for each row, but that doesn't seem to occur any more.
This is most visible for a lot of functions where the "group concat" trick ceased to work:
SELECT #sentence = #sentence + ' ' + word from SENTENCE_WORDS order by position
These have generally to be replaced by the xml concat trick.
set #sentence = (
select word as "text()", ' ' as "text()"
from SENTENCE_WORDS
order by position
for xml path(''), root('root'), type
).value('(/root)[1]', 'nvarchar(max)')
Remove the second ORDER BY (I.e. "order by number desc").
You are using an undocumented feature of T-SQL (I believe it's called ROW concatenation?) which is not guaranteed to work in future versions of SQL. It's a little hacky, but very useful none-the-less! As you've discovered, it breaks when you use the ORDER BY clause. This is a known issue of using Row concatenation.
I am trying to update a table I have with the windows version of a SQL Server using ##VERSION. When I run SELECT ##VERSION I get
Microsoft SQL Server 2012 - 11.0.5058.0 (X64)
May 14 2014 18:34:29
Copyright (c) Microsoft Corporation
Standard Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: ) (Hypervisor)
AND in this case I want "Windows NT 6.3" (14 characters long) that will never change I always want the 14 characters "Windows NT (some version)".
Sometime when I query some servers the "(Hypervisor)" Isn't there or the parentheses and text after those 14 characters is different. I want to automate this and I need to find a way to pull out just "Windows NT (some version)".
charindex() lets you find the location of substrings within a string. It also let's you specify a third argument to begin the search in the middle of the string which allows us to find a match relative to another location. We'll use that function several times.
We need to know the start of our desired string and the end of it. So we anchor our search to the string "Windows NT " which is 11 characters long. Then we want to find the next space character immediately following that match. It's necessary to add 11 to that value so it doesn't find the space characters inside the anchoring match (before and after the "NT".)
charindex('Windows NT ', ##version) /* start of match */
charindex(' ', ##version, charindex('Windows NT ', ##version) + 11) /* end of match */
Those two expressions identify the string offset but substring() needs to know the length to extract which means it needs to know the difference between the offsets. Since we don't want the final space character in the return value there's no need to add one to include it.
substring(
##version,
charindex('Windows NT ', ##version),
charindex(' ', ##version, charindex('Windows NT ', ##version) + 11)
- charindex('Windows NT ', ##version)
)
If you know it will always be Windows, you can use that fact to search where "on Windows" is in that string since it should always only ever be present once. Once you know the starting position you can use substring and adjust the parameters such that the string "on " is removed and then just pull your 14 characters.
IE:
declare #str varchar(500) = 'Microsoft SQL Server xxx (SPy) - 10.0.5500.0
(X64)
Sep 21 2011 22:45:45
Copyright (c) 1988-2008 Microsoft Corporation
Standard Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack
1) (VM)
'
select #str
select SUBSTRING(#str, CHARINDEX('on Windows', #str,1) + 3 ,14)
The application I am using is written by dinosaurs, using *= and =* operators in sql queries. These queries do joins on 10 or so tables, and I have about 500 such queries. And all I have time for is to upgrade the existing software as-is instead of writing any code. So, updating the query builder functions in all of them is not feasible.
Sadly, I am supposed to upgrade the application to use SQL server 2014, instead of 2005. And in 2014, the backward compatibility for a lot of operators is removed. So, I need to enable some kind of compatibility option in SQL server 2014, if it is available. But, I don't know if there is such a thing, and if it is, how to enable it.
Found a query to find the current compatibility setting. Posting it here, in case someone else needs it.
WITH compatversions AS (
SELECT 65 AS MajorVersion ,'SQL Server 6.5' AS ServerVersion
UNION
SELECT 70,'SQL Server 7.0'
UNION
SELECT 80 , 'SQL Server 2000'
UNION
SELECT 90 , 'SQL Server 2005'
UNION
SELECT 100 , 'SQL Server 2008/R2'
UNION
SELECT 110 , 'SQL Server 2012'
UNION
SELECT 120 , 'SQL Server 2014'
)
SELECT TOP 3 ServerVersion,MajorVersion
,ServerVersion + ' ('+ CONVERT(VARCHAR(3),MajorVersion) +')' AS DropDownFormat
FROM compatversions
WHERE MajorVersion IN (
SELECT TOP 3 MajorVersion
FROM compatversions
WHERE MajorVersion <= CONVERT(INT,CAST(##microsoftversion/ 0x1000000 AS VARCHAR(3)) + '0')
ORDER BY MajorVersion DESC)
ORDER BY MajorVersion ASC;
Apparently, SQL server 2014 supports up to 2008 compatibility.