Implications of || in outer joins - sql

the first code below brings the desired result but the second does not.
what are the implications of using the || operator?
First query:
SELECT
a.ID_CUSTOMER,
a.ID_VENDOR
FROM a
LEFT JOIN b
ON a.ID_CUSTOMER=b.ID_CUSTOMER
AND a.ID_VENDOR=b.ID_VENDOR
WHERE 1=1
AND b.ID_CUSTOMER IS NULL
AND b.VENDOR IS NULL
Second query:
SELECT
a.ID_CUSTOMER,
a.ID_VENDOR
FROM a
LEFT JOIN b
ON a.ID_CUSTOMER||a.ID_VENDOR=b.ID_CUSTOMER||b.ID_VENDOR
WHERE b.ID_CUSTOMER||b.ID_VENDOR IS NULL

|| is string concatenation. If any of the values are NULL, then the result is NULL, so it is equivalent to:
WHERE b.ID_CUSTOMER IS NULL OR b.ID_VENDOR IS NULL
In addition, the string concatenation has no "boundaries", so '123'||'456' matches '12'||'3456'.

Related

Cartesian product of two tables: Oracle Sql Join On True

I want to concatenate _BAR to the results of a Query.
One of my first attempts is this:
SELECT lhs.f||rhs.f as concat_bar FROM (
SELECT 'FOO' as f FROM DUAL) lhs
LEFT JOIN
(
SELECT '_BAR' as f FROM DUAL) rhs
ON ('' != rhs.f)
;
but I got no results. I was expecting ON ('' != rhs.f) to evaluate to true so I expected as a result a single row: 'FOO_BAR'. Which is the result of concatenating the cartesian product of the lhs and rhs tables.
How can I JOIN on TRUE?
I know that, for the specific problem, other solutions as
SELECT lhs.f||'_BAR' FROM (
SELECT 'FOO' as f FROM DUAL) lhs;
are possible.
My question is on an effective syntax to make a cartesian product of two tables as a LEFT JOIN ON TRUE.
Aside from the issues with null, is a cross join what you wanted?
select lhs.f || rhs.f as concat_bar
from ( select 'FOO' as f from dual ) lhs
cross join
( select '_BAR' as f from dual ) rhs
Oracle (by default) treats an empty string as NULL. This is a real pain. It is different from other databases. I suppose their argument is: "Well, we were doing it for years before ANSI defined NULL values." Great, but those years were the 1980s.
In any case, this logic:
'' <> rhs.f
(<> is the original not-equals operator in SQL.)
is exactly the same as:
NULL <> rhs.f
This always returns NULL and that is treated as not-true in WHERE conditions.
In Oracle, express this emptiness as:
rhs.f is not null

Not equal Null not working in sql server

I have a query given below
SELECT
B.[Date],
B.[Detail],
B.[Number],
B.[Total],
CASE WHEN B.[ApId] != null THEN Ap.Name ELSE B.[Name]END Name,
FROM [Bacs] B
LEFT JOIN Table ap ON b.ApId= ap.Id
In the Case When query I am trying to return the Name based on the b.ApId value.
But even if b.ApId is not null I am getting B.Name instead of Ap.Name.
Can anyone point out where I am going wrong.
!= will evaluate for values, while NULL is not a value.
So you have to either use IS NULL or IS NOT NULL to compare nulls.
SELECT
B.[Date],
B.[Detail],
B.[Number],
B.[Total],
CASE WHEN B.[ApId] is not null THEN Ap.Name ELSE B.[Name]END Name,
FROM [Bacs] B
LEFT JOIN Table ap ON b.ApId= ap.Id
Use IS NOT NULL, IS NULL to compare with NULL value, Or use ISNULL, COALESCE function
SELECT
B.[Date],
B.[Detail],
B.[Number],
B.[Total],
CASE WHEN B.[ApId] IS NOT NULL THEN Ap.Name ELSE B.[Name] END Name
-- ISNULL(Ap.Name, B.[Name]) as Name
-- COALESCE(Ap.Name, B.[Name],'') as Name
FROM [Bacs] B
LEFT JOIN Table ap ON b.ApId= ap.Id

If else condition in MSSQL

Suppose I have serial number, test name and few other columns, i want to write a condition if TESTNAME is null for a particular serial number then set the TESTNAME to blank else perform inner join
SELECT
(A.PTNUMBER + '-' +A.SL_NO) AS ENUMBER,
D.ENGINEER AS REQ, D.DATETIME as "DATE",
(select Value
from DROPDOWN
where B.TEST_NAME=CONVERT(VARCHAR,DropdownID)) TESTNAME,
TABLE_NAME AS TABLETD
FROM INSPECTION D
INNER JOIN TABLEA A ON D.ENGID = CONVERT(VARCHAR,A.EN_ID)
INNER JOIN TABLEB B ON B.ENGID = CONVERT(VARCHAR,A.EN_ID)
INNER JOIN TABLEC C ON C.ENGID = CONVERT(VARCHAR,A.EN_ID)
not sure what you mean by set testname to blank but if you meant to be using a SELECT query then you can do like
select *,
case when TESTNAME is null and serial_number = some_value then '' end as TESTNAME
from mytable
You could combine a case expression and coalesce() along with your join to choose the value you want to return.
select serial_number, ...
,case when coalesce(testname,'') <> ''
then t2.testname
else coalesce(testname,'') end
from t
inner join t2
on ...
You can use isnull() or coalesce() in sql server to return a different value to replace null.
select isnull(testname,'')
or
select coalesce(testname,'')
The main difference between the two is that coalesce() can support more than 2 parameters, and it selects the first one that is not null. More differences between the two are answered here.
select coalesce(testname,testname2,'')
coalesce() is also standard ANSI sql, so you will find it in most RDBMS. isnull() is specific to sql server.
Reference:
isnull() - msdn
coalesce() - msdn
SELECT (A.PTNUMBER + '-' + A.SL_NO) AS ENUMBER,
D.ENGINEER AS REQ,
D.DATETIME as "DATE",
case
when SerialNo = xxx and TESTNAME is null then ''
else (select Value from DROPDOWN where B.TEST_NAME = CONVERT(VARCHAR, DropdownID))
end AS TESTNAME,
TABLE_NAME AS TABLETD
FROM INSPECTION D
INNER JOIN TABLEA A ON D.ENGID = CONVERT(VARCHAR, A.EN_ID)
INNER JOIN TABLEB B ON B.ENGID = CONVERT(VARCHAR, A.EN_ID)
INNER JOIN TABLEC C ON C.ENGID = CONVERT(VARCHAR, A.EN_ID);

How to replace NULL value in select with subquery

Hi I'm performing a left join on two tables. If a particular column is NULL I want to run a subquery to get a value from a completely different table. Here's what I have now:
SELECT A.ACCOUNT_NUM, A.USER_ID,
CASE B.PREFERRED_NAME
WHEN '' THEN RTRIM(B.FIRST_NAME) || ' ' || B.LAST_NAME
ELSE RTRIM(B.PREFERRED_NAME) || ' ' || B.LAST_NAME
END AS NAME
FROM TABLE_A A
LEFT JOIN TABLE_B B
ON A.USER_ID = B.USER_ID
TABLE_B sometimes doesn't contain a record that matches with TABLE_A, so I want to run a subquery from TABLE_C that contains usernames and will match on A.USER_ID.
I thought I could do something like:
CASE B.PREFERRED_NAME
WHEN NULL THEN subquery here
But I get this error:
ERROR [42703] [IBM][DB2] SQL0206N "NULL" is not valid in the context where it is used.
Probably because NULLs are not allowed for that column.
SOLVED
Thanks for the help. This is how I solved my issue:
SELECT A.ACCOUNT_NUM, A.USER_ID,
CASE
WHEN B.PREFERRED_NAME IS NULL THEN C.USER_ID
WHEN B.PREFERRED_NAME IS NOT NULL THEN
CASE PREFERRED_NAME
WHEN '' THEN RTRIM(B.FIRST_NAME) || ' ' || B.LAST_NAME
ELSE RTRIM(B.PREFERRED_NAME) || ' ' || B.LAST_NAME
END
END AS NAME
FROM TABLE_A A
LEFT JOIN TABLE_B B
ON A.USER_ID = B.USER_ID
JOIN TABLE_C C
ON A.USER_ID = C.USER_ID
Depending on your query, you can probably just add your third table as another LEFT JOIN, then add the column you want to a COALESCE function:
Also, it looks like you're storing the preferred name as spaces if there isn't one, in which case you can use the NULLIF function to convert it to a NULL, which will work with your COALESCE.
Here's an example of what I mean:
SELECT
A.ACCOUNT_NUM
,A.USER_ID
,COALESCE(
NULLIF(B.PREFERRED_NAME,'')
,B.FIRST_NAME
,C.OTHER_NAME
) || ' ' || B.LAST_NAME AS NAME
FROM TABLE_A A
LEFT JOIN TABLE_C C
ON C.USER_ID = A.USER_ID
LEFT JOIN TABLE_B B
ON A.USER_ID = B.USER_ID
If you know there is always going to be a row in C that matches A, then you could convert that to a regular (inner) JOIN.
The reason you're getting the error, though is because you can't use NULL like that in a CASE statement. If you want to have a NULL case, then you have to do it like #Abecee said in the comment with CASE WHEN B.PREFERRED_NAME IS NULL THEN ...

Cross join on condition

I have a Stored Proc query below which involves returning partial delimited search string. E.g.searching passing a search string of 'wis,k' will return all results with ID that has 'wis' and 'k' in them. I am using a function and a cross join for this but the problem if attaching the cross join will prevent loading all my data which I will need to when I load this SPROC. I was thinking if a conditioned Cross Join is possible such that when my search string variable '#ReceiptNo' is null then I will omit the Cross Join and allow all my data to be displayed. Please kindly advice. Thanks.
Portion of my SPROC:
FROM [Transact] T
LEFT JOIN [Outlet] O On (T.Outlet_Code = O.Code)
LEFT JOIN [SystemCode] SC on (CONVERT(NVARCHAR,T.Mode) = SC.Code)
CROSS JOIN DBO.SPLIT(#ReceiptNo , ',') --SPLIT function to seperate delimited string
Where
(
CardNo In
(
Select [CardNo]
FROM [Card]
WHERE [CardNo] = #CardNo
AND [DeletedBy] IS NULL
AND [DeletedOn] IS NULL
AND [MemberID] = #MemberId
)
)
and
(
(T.TransactDate Between #TransactDateFrom And #TransactDateTo
or #TransactDateFrom is null
or #TransactDateTo is null
)
and (T.TransactDate >= #TransactDateFrom
or #TransactDateFrom is null)
and (T.TransactDate <= #TransactDateTo
or #TransactDateTo is null)
and
(
(',' + #Mode +',' LIKE '%,' + CONVERT(VARCHAR, T.Mode) + ',%')
or #Mode is null
)
and (T.ReceiptNo LIKE '%' + VAL + '%') --This is the 'LIKE' condition to return desired search string results
or (#TransactDateFrom is null
and #TransactDateTo is null
and #Mode is null
and #Outlet_Code is null
and #ReceiptNo is null
)
)
Group by T.AutoID, TransactDate,TransactTime, SC.Name, O.Name
, ReceiptNo, AmountSpent, TransactPoints, VoidOn
You need to take care of NULL and set it to any constant value. Modify CROSS JOIN to (read notes below query):
CROSS JOIN (SELECT ISNULL(Portion, 1) AS Portion FROM DBO.SPLIT(#ReceiptNo , ',')) TTT
In query above, Portion is column returned by DBO.SPLIT function. Change its name to appropriate and add more columns (with ISNULL) if needed.
Am I missing something or You can simply use LEFT JOIN instead of CROSS JOIN? Also, You might consider putting DBO.SPLIT function result into temporary table, index it and then use it in your CROSS/LEFT JOIN.
EDIT#1: I can't find any reason why You should not change CROSS JOIN to LEFT JOIN, as it will process less rows when #RecepitNo is not NULL.