Not able to compare columns in SQL Server - sql

I am using the following SQL code to compare two nvarchar columns. But the code is showing incorrect results:
SELECT
DP.NAME, DP.sid, SU.sid,
CASE
WHEN DP.sid = SU.sid
THEN 'TRUE'
ELSE 'FALSE'
END AS DESDIREDRESULT
FROM
#SQLLOGINS DP
INNER JOIN
SYS.sysusers SU ON DP.name COLLATE DATABASE_DEFAULT = SU.name COLLATE DATABASE_DEFAULT
In the code, I am doing a inner join of a temp table, #SQLLOGINS, with sys.sysusers. This temp table includes NAME and SID of sys.sqllogins.
I am facing an issue, while both the SID is same, it should be 'TRUE' in the output. Screenshot attached. But it's returning FALSE.
I am not sure where I am wrong here in comparing the SID columns.

You are mixing the types. Try this out:
DECLARE #mockupTable TABLE(ID INT IDENTITY,SomeString VARCHAR(100),SomeBinary VARBINARY(100));
INSERT INTO #mockupTable VALUES('0x1234',0x1234);
INSERT INTO #mockupTable VALUES(0x6565,0x6565); --implicit cast!
INSERT INTO #mockupTable VALUES('ee', CAST('ee' AS VARBINARY(100))) --explicit cast!
SELECT *, CASE WHEN SomeString=SomeBinary THEN 'TRUE' ELSE 'FALSE' END FROM #mockupTable;
The result
+----+------------+------------+--------------------+
| ID | SomeString | SomeBinary | |
+----+------------+------------+--------------------+
| 1 | 0x1234 | 0x1234 | FALSE |
+----+------------+------------+--------------------+
| 2 | ee | 0x6565 | TRUE |
+----+------------+------------+--------------------+
| 3 | ee | 0x6565 | TRUE |
+----+------------+------------+--------------------+
What happens here?
The first row looks the same, but isn't, while the 2 and 3 are obviously different but return they are the same?
The reason: The binary value 0x1234 and the string 0x1234 are not the same, although the look as if they were.
Just try SELECT CAST('0x1234' AS VARBINARY(100)). The result is 0x307831323334 which is - obviously! - not the same as 0x1234. It is a list of codes actually: 30 (0), 78 (x), 31 (1), 32 (2), 33 (3), 34 (4).
But in row 2 and 3 you can see, that the binary value of a string can be compared with a real binary. Doing this, you can see that the string ee has two small letter e, with the ASCII code 65. So 0x6565 translates to ee.

There are typically 3 causes of things like this:
Hidden characters (line feeds etc)
Incompatible data types or code pages
Trailing spaces
I suggest you cast/convert both attributes and throw a trim in for good measure.

Related

SQL multipart messages in two tables. Doesn't work if second table is empty

I am working on a query that will fetch multipart messages from 2 tables. However, it only works IF there are multiple parts. If there is only a one part message then the the join condition won't be true anymore. How could I make it to work for both single and multipart messages?
Right now it fails if there is an entry in outbox and nothing in outbox_multipart.
My first table is "outbox" that looks like this.
TextDecoded | ID | CreatorID
Helllo, m.. | 123 | Martin
Yes, I wi.. | 124 | Martin
My second table is "outbox_multipart" that looks very similar.
TextDecoded | ID | SequencePosition
my name i.. | 123 | 2
s Martin. | 123 | 3
ll do tha.. | 124 | 2
t tomorrow. | 124 | 3
My query so far
SELECT
CONCAT(ob.TextDecoded,
GROUP_CONCAT(obm.TextDecoded
ORDER BY obm.SequencePosition ASC
SEPARATOR ''
)
) AS TextDecoded,
ob.ID,
ob.creatorID
FROM outbox AS ob
JOIN outbox_multipart AS obm ON obm.ID = ob.ID
GROUP BY
ob.ID,
ob.creatorID
Use a left join instead of an (implicit) inner join. Then, also use COALESCE on the TextDecoded alias to make sure that empty string (and not NULL) appears in the expected output.
SELECT
CONCAT(ob.TextDecoded,
COALESCE(GROUP_CONCAT(obm.TextDecoded
ORDER BY obm.SequencePosition
SEPARATOR ''), '')) AS TextDecoded,
ob.ID,
ob.creatorID
FROM outbox AS ob
LEFT JOIN outbox_multipart AS obm
ON obm.ID = ob.ID
GROUP BY
ob.ID,
ob.creatorID,
ob.TextDecoded;
Note: Strictly speaking, outbox.TextDecoded should also appear in the GROUP BY clause, since it is not an aggregate. I have made this change in the query.

Empty Char in Where Clause?

I have the following table:
CREATE TABLE SOAUDIT
(SOU_USER CHAR(8 BYTE),
SOU_ORDREF CHAR(8 BYTE),
SOU_TYPE CHAR(1 BYTE),
SOU_DESC CHAR(50 BYTE))
There is a unique index defined on the first three columns (but no primary key, which is something we have no control over).
And in the table there are some records:
| SOU_USER | SOU_ORDREF | SOU_TYPE | SOU_DESC |
|----------|------------|----------|------------------|
| proust | | S | recherche |
| joyce | 12345678 | S | pelurious |
| orwell | 19841984 | T | doubleplusungood |
| camus | 34598798 | P | peiner |
On closer inspection it appears that the value in SOU_ORDREF for user 'proust' is an empty char string of 8 characters.
Now, what I need to be able to do is to query this table based on their unique values (which I will receive from a SQL Server database (just to complicate matters nicely). In the case of SOU_ORDREF the search value will be a blank field:
SELECT *
FROM SOAUDIT
WHERE (SOU_USER, TRIM(SOU_ORDREF), SOU_TYPE)
IN (('proust', null, 'S'))
This doesn't return the record I am looking for.
When I rewrite the query as following:
SELECT *
FROM SOAUDIT
WHERE (SOU_USER, SOU_TYPE)
IN (('proust', 'S'))
AND TRIM(sou_ordref) is null
Then I do get the desired record.
However, I want to be able to pass in more than one record into the WHERE clause so the second version doesn't really help.
Oracle -- by default -- treats empty strings and NULL as the same thing.
This can cause awkward behavior, because comparisons to NULL almost never return true. So a simple expression such as where sou_ordref = '' never returns true, because it is equivalent to where sou_ordref = NULL.
Here is one workaround:
SELECT *
FROM SOAUDIT
WHERE (SOU_USER, COALESCE(TRIM(SOU_ORDREF), ' '), SOU_TYPE) IN
( ('proust', ' ', 'S') )
Note that this replaces the empty string (NULL) with a space. It then compares the results to a space.
Try this way:
SELECT *
FROM test
WHERE SOU_USER = 'proust'
AND SOU_TYPE = 'S'
AND TRIM(sou_ordref) = ''
Since an empty char is different than NULL

PSQL select comma delimited results into join

How would you go about returning the key_results, based on the key_id's listed in this comma delimited key_list?
SELECT key_list FROM some.place where key_number=1234;
key_list
----------------
{32,35,58,63,89}
SELECT key_id, key_result FROM some.otherplace;
key_id | key_result
--------------------------
32 | frisbee
33 | duckhunt
34 | hairplugs
35 | sparkplugs
Because your string is a valid PostgreSQL array literal, you can use it as input to = ANY with a cast to integer[]:
SELECT o.key_id, o.key_result
FROM some.otherplace o
INNER JOIN some.place p ON (o.key_id = ANY (key_list::integer[]))
Really though ... this is bad schema design. For why, see this post. At minimum you should store an actual array.
Other useful tools when dealing with comma-separated values are string_to_array, unnest, and regexp_split_to_table.

Getting warning: Null value is eliminated by an aggregate or other SET operation

I have this schema
create table t(id int, d date)
insert into t (id, d) values (1, getdate()),
(2, NULL)
When doing
declare #mindate date
select #mindate = min(d) from t
I get the warning
Null value is eliminated by an aggregate or other SET operation
Why and what can I do about it?
Mostly you should do nothing about it.
It is possible to disable the warning by setting ansi_warnings off but this has other effects, e.g. on how division by zero is handled and can cause failures when your queries use features like indexed views, computed columns or XML methods.
In some limited cases you can rewrite the aggregate to avoid it. e.g. COUNT(nullable_column) can be rewritten as SUM(CASE WHEN nullable_column IS NULL THEN 0 ELSE 1 END) but this isn't always possible to do straightforwardly without changing the semantics.
It's just an informational message required in the SQL standard. Apart from adding unwanted noise to the messages stream it has no ill effects (other than meaning that SQL Server can't just bypass reading NULL rows, which can have an overhead but disabling the warning doesn't give better execution plans in this respect)
The reason for returning this message is that throughout most operations in SQL nulls propagate.
SELECT NULL + 3 + 7 returns NULL (regarding NULL as an unknown quantity this makes sense as ? + 3 + 7 is also unknown)
but
SELECT SUM(N)
FROM (VALUES (NULL),
(3),
(7)) V(N)
Returns 10 and the warning that nulls were ignored.
However these are exactly the semantics you want for typical aggregation queries. Otherwise the presence of a single NULL would mean aggregations on that column over all rows would always end up yielding NULL which is not very useful.
Which is the heaviest cake below? (Image Source, Creative Commons image altered (cropped and annotated) by me)
After the third cake was weighed the scales broke and so no information is available about the fourth but it was still possible to measure the circumference.
+--------+--------+---------------+
| CakeId | Weight | Circumference |
+--------+--------+---------------+
| 1 | 50 | 12.0 |
| 2 | 80 | 14.2 |
| 3 | 70 | 13.7 |
| 4 | NULL | 13.4 |
+--------+--------+---------------+
The query
SELECT MAX(Weight) AS MaxWeight,
AVG(Circumference) AS AvgCircumference
FROM Cakes
Returns
+-----------+------------------+
| MaxWeight | AvgCircumference |
+-----------+------------------+
| 80 | 13.325 |
+-----------+------------------+
even though technically it is not possible to say with certainty that 80 was the weight of the heaviest cake (as the unknown number may be larger) the results above are generally more useful than simply returning unknown.
+-----------+------------------+
| MaxWeight | AvgCircumference |
+-----------+------------------+
| ? | 13.325 |
+-----------+------------------+
So likely you want NULLs to be ignored, and the warning just alerts you to the fact that this is happening.
#juergen provided two good answers:
Suppress the warning using SET ANSI_WARNINGS OFF
Assuming you want to include NULL values and treat them as (say) use select #mindate = min(isnull(d, cast(0 as datetime))) from t
However if you want to ignore rows where the d column is null and not concern yourself with the ANSI_WARNINGS option then you can do this by excluding all rows where d is set to null as so:
select #mindate = min(d) from t where (d IS NOT NULL)
I think you can ignore this warning in the case since you using the MIN function.
"Except for COUNT, aggregate functions ignore null values"
Please refer Aggregate Functions (Transact-SQL)
What should min() return in your case as lowest value of d?
The error informs you that the min() function did not take records into account that are null.
So if it should ignore the NULL values and return the lowest existing date then you can ignore this warning.
If you also like to suppress warnings for this single statement then you can do it like this
set ansi_warnings off
select #mindate = min(d) from t
set ansi_warnings on
If you want NULL values taken into account by using a default value for them then you can set a default date value like this
select #mindate = min(isnull(d, cast(0 as datetime)))
from t
If you want to make aggregates consider null values and treat the result as null you can use:
SELECT IIF(COUNT(N) != COUNT(*), NULL, SUM(N)) as [Sum]
FROM (VALUES (NULL),
(3),
(7)) V(N)
This returns null if not all values are given.

How to convert data from ASCII to Unicode (correct collation)?

Hi I have problem that I'm dealing first time. I have MS SQL Server Databse with collation Latin1_General_CS_AS (code 1252). But there data is actually in Lithuanian language 'Lithuanian_CS_AS' (code 1257). I need to get data and write in another database, converted in 'normal' strings - probably unicode. I'm using also SSIS if this can help resolve problem.
Data:
B×kÑiÖ g. 11-12:
print ASCII(SUBSTRING(#string, #position, 1))
print CHAR(ASCII(SUBSTRING(#string, #position, 1)))
66 B | 215 × | 107 k | 209 Ñ | 105 i | 214 Ö | 32 | 103 g | 46 . | 32 | 49 1 | 49 1 | 45 - | 49 1 | 50 2
Using this ASCII page of codes http://www.ascii-codes.com/cp775.html it must be converted to:
Result:
Būkčių g. 11-12
Maybe there are sql query which can convert B×kÑiÖ g. 11-12 --> Būkčių g. 11-12 ?
Some SQLFIDDLESampleData if you can convert this in Lithuanian Language. I would like to solve this easy, without converting each character in 'Lithuanian' language because this would be insane :)
More Info
Database with collation Latin1_General_CS_AS users using with software (reports, forms), where everything is ok in Lithuanian language (I don't know how they can see correct letters). I'm using MS SQL Server 2008 r2.
I can be not always near computer, but I try to answer all your questions. Thank you.
How about the COLLATE clause?
From MSDN:
Specifying collation during a select
The following example creates a simple table and inserts 4 rows. Then the example applies two collations when selecting data from the table, demonstrating how Chiapas is sorted differently.
CREATE TABLE Locations
(Place varchar(15) NOT NULL);
GO
INSERT Locations(Place) VALUES ('Chiapas'),('Colima')
, ('Cinco Rios'), ('California');
GO
--Apply an typical collation
SELECT Place FROM Locations
ORDER BY Place
COLLATE Latin1_General_CS_AS_KS_WS ASC;
GO
-- Apply a Spanish collation
SELECT Place FROM Locations
ORDER BY Place
COLLATE Traditional_Spanish_ci_ai ASC;
GO
I have come to a decision use functions: REPLACE CHAR
In my case varchar was datatype for columns. So to convert to proper language words in unicode (nvarchar) steps:
Convert the column in unicode with CS and AS atributtes in select statement:
SELECT cast(column Collate Latin1_General_CS_AS as nvarchar(22))
2.And use replace statement to convert in real letters code::
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(cast(column Collate Latin1_General_CS_AS as nvarchar(22)),CHAR(181),N'Ą'), CHAR(208), N'ą'),CHAR(182),N'Č'), CHAR(209), N'č'), CHAR(183), N'Ę'), CHAR(210), N'ę'),CHAR(184), N'Ė'), CHAR(211), N'ė'),CHAR(189), N'Į'),CHAR(212), N'į'), CHAR(190), N'Š'), CHAR(213), N'š'), CHAR(198), N'Ų'), CHAR(214), N'ų'),CHAR(199), N'Ū'), CHAR(215), N'ū'), CHAR(207), N'Ž'), CHAR(216), N'ž')
Result:
| ¾ilutÓs pl. 83 | Šilutės pl. 83 |
| B×kÑiÖ g. 11 | Būkčių g. 11 |
| Seni×kÖ km. | Seniūkų km. |