Is there a rule for naming columns a certain name? - sql-server

So we have a SQL Server 2017 instance with the default collation of Vietnamese_100_CI_AS_KS.
When we create a Temp Table, we see that if a specific name is used, it somehow sees one letter as case sensitive.
Example:
CREATE TABLE #MaterialTest
(
Product VARCHAR(50),
Quality VARCHAR (50),
MaterialOriginGroup VARCHAR (50)
)
INSERT INTO #MaterialTest VALUES ('Papers', 'Good', 'Test1')
If I query this table using:
Select Product, Quality, MaterialOriginGroup from #MaterialTest
It works fine. If I query it using pretty much anything else it works fine
But if I use a lower case "G" for "Group" in MaterialOriginGroup it fails.
Example:
Use TempDB
Select product, quality, materialorigingroup from #MaterialTest
Msg 207, Level 16, State 1, Line 2 Invalid column name
'materialorigingroup'.
If I query it with:
Use TempDB
Select product, quality, materialoriginGroup from #MaterialTest
It works.
Any idea why?

Building on my own investigations and #Larnu's SQL Fiddle, I've devised a little script to test two-letter combinations:
DECLARE #Letter VARCHAR(2);
SET #Letter = 'ch';
DECLARE #LetterA VARCHAR(1);
DECLARE #LetterB VARCHAR(1);
SET #LetterA = LEFT(#Letter, 1);
SET #LetterB = RIGHT(#Letter, 1);
SELECT CASE WHEN V.G = UPPER(#Letter) COLLATE Vietnamese_100_CI_AS_KS THEN 1 ELSE 0 END,
CASE WHEN V.G = LOWER(#Letter) COLLATE Vietnamese_100_CI_AS_KS THEN 1 ELSE 0 END,
CASE WHEN V.G = CONCAT(LOWER(#LetterA), UPPER(#LetterB)) COLLATE Vietnamese_100_CI_AS_KS THEN 1 ELSE 0 END,
CASE WHEN V.G = CONCAT(UPPER(#LetterA), LOWER(#LetterB)) COLLATE Vietnamese_100_CI_AS_KS THEN 1 ELSE 0 END
FROM (VALUES(#Letter COLLATE Vietnamese_100_CI_AS_KS)) V(G);
This will output 1 or 0 depending upon whether a comparison is valid for the two letter combination. The comparisons are:
Upper case letters (i.e. NG)
Lower case letters (i.e. ng)
One upper case, one lower case letter (i.e. Ng)
One lower case, one upper case letter (i.e. nG)
Running this for ab produces:
1 1 1 1
So ab does not exhibit this issue. However, for ch you get:
1 1 0 1
So ch does exhibit the same issue.
This seems related to declared consonants in the Vietnamese alphabet, which include these two-letter consonants:
Ch = 1 1 0 1 (has the same issue)
Gh = 1 1 0 1 (has the same issue)
Gi = 1 1 0 1 (has the same issue)
Kh = 1 1 0 1 (has the same issue)
Ng = 1 1 0 1 (has the same issue)
Nh = 1 1 0 1 (has the same issue)
Ph = 1 1 0 1 (has the same issue)
Qu = 1 1 0 1 (has the same issue)
Th = 1 1 0 1 (has the same issue)
Tr = 1 1 0 1 (has the same issue)
Unfortunately, I do not know why these are affected by this issue, which is not helpful. In addition, I don't know why it only affects the column name when it is a lowercase followed by an uppercase letter.
Perhaps someone else may see this and know the answer?
The reference for the alphabet is here.

Related

SQL Equivalent of Countif or Index Match on row level instead of columnar level

I have a table which comprises of 30 columns, all adjacent to one another. Of these 5 are text fields indicating certain details pertaining to that entry and 25 are value fields. Value fields have the column name as Val00, Val01, Val02 .....upto Val24
Based on a logic appearing elsewhere, these value fields input a value for n amount of columns and then drop to 0 for all of the subsequent fields
e.g.
When n is 5 the output will be
Val00
Val01
Val02
Val03
Val04
Val05
Val06
Val07
Val24
1.5
1.5
1.5
1.5
1.5
0
0
0
0
As can be seen, all values starting from val05 will drop to 0 and all columns from Val 05 to Val24 will be 0.
Given this output, I want to find what this n value is and create a new column ValCount to store this value in it.
In Excel this would be fairly straight forward to achieve with the below formula
=COUNTIF(Val00:Val24,">0")
However I'm not sure how we would go about in terms of SQL. I understand that the count function works on a columnar level and not on a row level.
I found a solution which is rather long but it should do the job so I hope it helps you.
SELECT SUM(SUM(CASE WHEN Val00 >= 1 THEN 1 ELSE 0 END)
+ SUM(CASE WHEN Val01 >= 1 THEN 1 ELSE 0 END)
+ SUM(CASE WHEN Val02 >= 1 THEN 1 ELSE 0 END)) As ValCount

Using between clause on nvarchar column to find range is not working

I have 3 nvarchar columns user_3, user_4 and description. I am setting yes and no flag. If the value in description column is equal or in between user_3 and User4 then set the flag to 'N' else
set the flag to 'Y'.
Here is the SQL script I wrote so far. it works in some instances but not always. See the image with results. it worked on line #1 but didn't work on line # 6 for example. What am I doing wrong?
SELECT [B].USER_3,[B].USER_4,A.DESCRIPTION,
(case when Isnumeric(A.DESCRIPTION) <> 1 then 'Y'
else case when (CASE WHEN Isnumeric(A.DESCRIPTION) = 1 then
cast(A.DESCRIPTION AS decimal(10,5)) else 0 end)
between ( CASE WHEN Isnumeric([B].USER_4) = 1 then
cast([B].USER_4 AS decimal(10,5)) else 0 end) and
(CASE WHEN Isnumeric([B].USER_3) = 1 then cast([B].USER_3 AS decimal(10,5)) else 0 end)
then 'N' else 'Y' end end) as Flagset
from A , B
Here is the screenshot of the results
enter image description here
The issue is with your use of BETWEEN as per the docs:
BETWEEN returns TRUE if the value of test_expression is greater than or equal to the value of begin_expression and less than or equal to the value of end_expression.
Because you don't know whether USER_3 or USER_4 is the higher limit or the lower limit, you need to test both ways.
Note: For this sort of query I prefer to pre-calculate all the values (using CROSS APPLY in this case) I need. It makes it much easier to follow and debug.
SELECT USER_3, USER_4, [DESCRIPTION]
, CASE WHEN ISNUMERIC([DESCRIPTION]) <> 1 THEN 'Y' ELSE
CASE WHEN CASE WHEN ISNUMERIC([DESCRIPTION]) = 1 THEN CAST([DESCRIPTION] AS decimal(10,5)) ELSE 0 END BETWEEN CASE WHEN ISNUMERIC(USER_4) = 1 THEN CAST(USER_4 AS decimal(10,5)) ELSE 0 END AND
CASE WHEN ISNUMERIC(USER_3) = 1 THEN CAST(USER_3 AS decimal(10,5)) ELSE 0 END
THEN 'N' ELSE 'Y' END END AS Flagset
, CASE WHEN DNUMERIC <> 1 THEN 'Y' ELSE CASE WHEN DESCRIPTIOND BETWEEN USER_4D AND USER_3D OR DESCRIPTIOND BETWEEN USER_3D AND USER_4D THEN 'N' ELSE 'Y' END END CorrectedFlagSet
FROM (VALUES
('1.395','1.385','1.390')
, ('22.025','41.425','22')
, ('22.025','41.425','23.025')
) AS X (USER_3, USER_4, [DESCRIPTION])
CROSS APPLY (VALUES (
CASE WHEN ISNUMERIC(USER_3) = 1 THEN CAST(USER_3 AS decimal(10,5)) ELSE 0 END
, CASE WHEN ISNUMERIC(USER_4) = 1 THEN CAST(USER_4 AS decimal(10,5)) ELSE 0 END
, CASE WHEN ISNUMERIC([DESCRIPTION]) = 1 THEN CAST([DESCRIPTION] AS decimal(10,5)) ELSE 0 END
, CASE WHEN ISNUMERIC([DESCRIPTION]) = 1 THEN 1 ELSE 0 END
)) AS Y (USER_3D, USER_4D, DESCRIPTIOND, DNUMERIC);
Returns:
USER_3
USER_4
DESCRIPTION
Flagset
CorrectedFlagSet
1.395
1.385
1.390
N
N
22.025
41.425
22
Y
Y
22.025
41.425
23.025
Y
N
I'm sure I don't have to mention that you should really be storing this data in numeric form in the first place as it will perform better and save you lots of future issues.
And well laid out queries with consistent casing also helping understand and debug them.
Finally providing a minimal reproducible example with sample data, your query and your desired result as shown here makes it much easier for people to assist.

ASCII Comparisons when Using SQL_Latin1_General_CP1_CI_AS

I ran the query below to see how strings would compare in SQL Server, since a different query based on comparison of fields with this type of data yielded different results than expected. I wasn't sure whether the angle bracket would be considered a lower value than a number (based on the ASCII table it would not, but wanted to check). As a result of the outcome, I've reviewed multiple posts regarding string comparison, collation and expected values and they all seem to reinforce that this should not be working this way. The collation on the database (as on the field that caused trouble) is SQL_Latin1_General_CP1_CI_AS.
SELECT ASCII('<') AS [<]
,ASCII('0') AS [0]
,CASE WHEN '<0.1' < '0.1' THEN 1 ELSE 0 END AS TEST1
,CASE WHEN '<' < '0' THEN 1 ELSE 0 END AS TEST2
,CASE WHEN ASCII('<') < ASCII('0') THEN 1 ELSE 0 END AS TEST3
Result:
< 0 TEST1 TEST2 TEST3
60 48 1 1 0
Any ideas to point me in the right direction would be very much appreciated!

Dutch zipcodes regex in SQL Server 2014 always returns 0

I might be missing something. I have to check Dutch zipcodes, but I got some user entered data in my database. I want to check if the zipcode can be an actual zipcode. Format for Dutch zipcodes: 1000-9999AA-ZZ
So any integer between 1000 and 9999 in combination with 2 lettres can be a valid zipcode (there are some additional parameters, but I am not worrying about them for now).
I didn't get my regex to work with this code:
iif(ZipCode like '^[1-9][0-9]{3}\s[a-zA-Z]{2}$',1,0) as MatchIndicator
Yet it always returns zero.
I even tried it with a simpler regex
iif(ZipCode like '^[1-9]',1,0) as MatchIndicator
Returns 0 everytime as well.
I found myself an alternative, but I think the regex code is better to use in the long run for more complicated text.
Alternative
case when LEFT(ZipCode,1) between '1' and '9'
and substring(ZipCode,2,1) between '0' and '9'
and substring(ZipCode,3,1) between '0'and '9'
and substring(ZipCode,4,1) between '0' and '9'
and substring(ZipCode,5,1) between 'A' and 'Z'
and substring(ZipCode,6,1) between 'A' and 'Z' then 1 else 0 end as MatchIndicator
And
patindex('[1-9][0-9][0-9][0-9][a-zA-z][a-zA-z]',ZipCode)
Any thoughts?
SQL Server doesn't support 'proper' regex. So how about:
CREATE TABLE #Test (Postcode VARCHAR(6))
INSERT INTO #Test
VALUES
('1234AZ'),
('9876ZQ'),
('1900Sz'),
('ABCDe1'),
('XwYx1A'),
('5000A1')
SELECT
PostCode,
CASE WHEN
TRY_CAST(SUBSTRING(PostCode, 1, 4) AS INT) BETWEEN 1000 AND 9999
AND
PATINDEX('%[A-Z][A-Z]%' COLLATE Latin1_General_Bin, SUBSTRING(PostCode, 5, 2)) > 0 THEN 1 ELSE 0
END IsValid
FROM #Test
PostCode IsValid
-------- -----------
1234AZ 1
9876ZQ 1
1900Sz 0
ABCDe1 0
XwYx1A 0
5000A1 0

Check Constraints and Case Statement

I need help with this check constraint, I get the following error message: "Msg 102, Level 15, State 1, Line 14
Incorrect syntax near '='."
Or maybe the question I should ask is if this is possible using a check constraint
What I am trying to achieve is: If InformationRestricted is True, InformationNotRestricted cannot be true and InformationRestrictedFromLevel1, InformationRestrictedFromLevel2, InformationRestrictedFromLevel3, InformationRestrictedFromLevel4, InformationRestrictedFromLevel5 cannot be true
I am not trying to assign values to the columns, just trying to ensure the values of the columns = 0 (i.e. false) if InformationRestricted is True
Here is the script:
CREATE TABLE EmployeeData
(FirstName varchar(50),
Last Name varchar(50),
Age int,
Address varchar(100),
InformationRestricted bit,
InformationNotRestricted bit,
InformationRestrictedFromLevel1 bit,
InformationRestrictedFromLevel2 bit
InformationRestrictedFromLevel3 bit
InformationRestrictedFromLevel4 bit
InformationRestrictedFromLevel5 bit);
ALTER TABLE EmployeeData ADD CONSTRAINT ck_EmployeeData
CHECK (CASE WHEN InformationRestricted = 1 THEN InformationNotRestricted = 0 --InformationRestricted is true, InformationNotRestricted is false
AND( InformationRestrictedFromLevel1 = 0 --is false
OR InformationRestrictedFromLevel2 = 0 --is false
OR InformationRestrictedFromLevel3 = 0 --is false
OR InformationRestrictedFromLevel4 = 0 --is false
OR InformationRestrictedFromLevel5 = 0)); --is false
A CASE expression is something that returns a value of a particular data type (the type to be determined by the various datatypes of each THEN clause).
SQL Server doesn't have a boolean data type, so you can't return the result of a comparison operation.
Try adding additional comparisons into WHEN clauses, and having the THENs return either 1 or 0, if you want to allow or disallow the outcome (respectively). Then compare the overall result to 1.
I can't parse out the sense of your condition entirely, but something like:
CHECK(CASE WHEN InformationRestricted = 1 THEN
CASE WHEN InformationNotRestricted = 0 AND
(InformationRestrictedFromLevel1 = 0 --is false
OR InformationRestrictedFromLevel2 = 0 --is false
OR InformationRestrictedFromLevel3 = 0 --is false
OR InformationRestrictedFromLevel4 = 0 --is false
OR InformationRestrictedFromLevel5 = 0)
THEN 1
ELSE 0
END
--Other conditions?
END = 1)
My confusion is I'd have though you'd want to check that one and only one of the InformationRestrictedFromXXX columns would be one. In fact, from the general description, (without knowing more about your problem domain), I'd have probably just created a column InformationRestrictionLevel, of type int, with 0 meaning unrestricted, and higher values indicating the level it's restricted from.
Looks like you're not closing the case with end. The basic format of a check constraint using case is:
check(case when <condition> then 1 else 0 end = 1)
If you nest multiple cases, be sure to match the number of cases with the number of ends:
check
(
1 =
case
when <condition> then
case
when <condition> then 1
else 0
end
else 0
end
)
Formatting all elements of the same case with the same indentation can be a big help.

Resources