"Invalid use of Null" suddenly from 2nd left join - sql

I have this SQL query in Access, which works fine:
SELECT TableA.FieldA As [Code],
Count(TableA.FieldC) AS [Count]
FROM ((MainTable)
LEFT JOIN TableA ON MainTable.FieldB = TableA.FieldB)
WHERE (((MainTable.DateOf)>=#1/1/2012#))
AND Clng(TableA.FieldA) >= 119593451
AND Clng(TableA.FieldA) <= 119593461
GROUP BY TableA.FieldA;
But when I try another left join, like so:
SELECT TableA.FieldA As [Code],
Count(TableA.FieldC) AS [Count]
FROM ((MainTable)
LEFT JOIN TableA ON MainTable.FieldB = TableA.FieldB)
LEFT JOIN TableB ON TableA.FieldD = TableB.FieldD
WHERE (((MainTable.DateOf)>=#1/1/2012#))
AND Clng(TableA.FieldA) >= 119593451
AND Clng(TableA.FieldA) <= 119593461
GROUP BY TableA.FieldA;
I am using the parenthesis in the FROM claused based on this: http://nm1m.blogspot.com/2007/10/multiple-left-joins-in-ms-access.html
It gives the error Invalid use of Null, which doesn't make sense to me as I'm performing no null check etc. What is the problem here? I'm trying to pull a field in TableB to display (but did not put it in the select section yet).

Access likes its parentheses. Add more parentheses around joins.
The CLng function requires a non-null value. You can fix this with CLng(Nz(TableA.FieldA, "0")) >= 119593451. Though, if the field is numeric, why on earth would you make it text to begin with? This is a serious problem that I highly recommend fixing immediately (if at all possible) by changing the data type to a numeric one.
But you have another problem. It is meaningless to LEFT JOIN to a table if you then in the WHERE clause put a condition, as you are doing in your query! Either change to an INNER JOIN or put your conditions on TableA into the ON clause of the LEFT JOIN. If Access won't allow this syntax, then change to a derived table:
SELECT
TableA.FieldA As [Code],
Count(TableA.FieldC) AS [Count]
FROM
(
(
(MainTable)
LEFT JOIN (
SELECT * FROM TableA
WHERE
Clng(Nz(TableA.FieldA, "0")) >= 119593451
AND Clng(Nz(TableA.FieldA, "0")) <= 119593461
) TableA ON MainTable.FieldB = TableA.FieldB
)
LEFT JOIN TableB ON TableA.FieldD = TableB.FieldD
)
WHERE (((MainTable.DateOf)>=#1/1/2012#))
GROUP BY TableA.FieldA;
Note: adding WHERE TableA.FieldA IS NOT NULL may work but may not work. In Access it could be 100% safe but such a query in SQL Server would NOT be safe because there is no guaranteeing the order of conditions being applied. Obviously, in SQL Server you can convert to an integer even when NULL and there is no CLng function, but the point is still valid: do not get in the habit of relying on some imagined order of WHERE conditions protecting you from invalid type conversion: instead you must handle the invalid type conversion inside the expression that can error--that is best practice.

Related

Can I use WHERE clause after JOIN USING in snowflake?

Can I use WHERE after
JOIN USING?
In my case if I run on snowflake multiple times the same code:
with CTE1 as
(
select *
from A
left join B
on A.date_a = B.date_b
)
select *
from CTE1
inner join C
using(var1_int)
where CTE1.date_a >= date('2020-10-01')
limit 1000;
sometimes I get a result and sometimes i get the error:
SQL compilation error: Can not convert parameter 'DATE('2020-10-01')' of type [DATE] into expected type [NUMBER(38,0)]
where NUMBER(38,0) is the type of var1_int column
Your problem has nothing to do with the existence of a where clause. Of course you can use a where clause after joins. That is how SQL queries are constructed.
According to the error message, CTE1.date_a is a number. Comparing it to a date results in a type-conversion error. If you provided sample data and desired results, then it might be possible to suggest a way to fix the problem.
tl;dr: Instead of JOIN .. USING() always prefer JOIN .. ON.
You are right to be suspicious of the results. Given your staging, only one of these queries returns without errors:
select a.date_1, id_1
from AE_USING_TST_A a
left join AE_USING_TST_B b
on a.date_1 = b.date_2
join AE_USING_TST_C v
using(id_1)
where A.date_1 >= date('2020-10-01')
-- Can not convert parameter 'DATE('2020-10-01')' of type
-- [DATE] into expected type [NUMBER(38,0)]
;
select a.date_1, a.id_1
from AE_USING_TST_A a
left join AE_USING_TST_B b
on a.date_1 = b.date_2
join AE_USING_TST_C v
on a.id_1=v.id_1
where A.date_1 >= date('2020-10-01')
-- 2020-10-11 2
;
I would call this a bug, except that the documentation is clear about not doing this kind of queries with JOIN .. USING:
To use the USING clause properly, the projection list (the list of columns and other expressions after the SELECT keyword) should be “*”. This allows the server to return the key_column exactly once, which is the standard way to use the USING clause. For examples of standard and non-standard usage, see the examples below.
https://docs.snowflake.com/en/sql-reference/constructs/join.html
The documentation doubles down on the problems of using USING() on non-standard situations, with a different query acting "wrong":
The following example shows non-standard usage; the projection list contains something other than “*”. Because the usage is non-standard, the output contains two columns named “userid”, and the second occurrence (which you might expect to contain a value from table ‘r’) contains a value that is not in the table (the value ‘a’ is not in the table ‘r’).
So just prefer JOIN .. ON. For extra discussion on the SQL ANSI standard not defining behavior for some cases of USING() check:
https://community.snowflake.com/s/question/0D50Z00008WRZBBSA5/bug-with-join-using-

Sql select - how select from other table if is null

I have a view which returns me some nulls values for the columns b.emissor and B.indexador. In case of null, I need to find this values first in table TB_CAD_RF and, if still nulls, I need to query TB_CAD_RF_2.
I try the logic below but its not working. Also tried to think something with case statements but cant figure it out.
Anyone could help me please?
select A.NM_ATIVO, B.EMISSOR, B.INDEXADOR from VW_POSICAO as A
LEFT JOIN
TB_CAD_RF B on A.NM_ATIVO = B.CODIGO
where a.NM_EMISSOR is null
as C LEFT JOIN (
select C.EMISSOR, C.INDEXADOR from TB_CAD_RF_2 as D ON B.NM_ATIVO = C.CODIGO where C.EMISSOR is null)
This pattern:
SELECT
COALESCE( first.choice, second.choice, third.choice) as a
FROM
first
LEFT JOIN second on first.id = second.id
LEFT JOIN third on second.id = third.id
Coalesce returns the first non null passed into it, scanning from left to right
Here is one way. Always join both and coalesce the fields in the order of desired results.
select A.NM_ATIVO,
EMISSOR = COALESCE(A.EMISSOR,B.EMISSOR),
INDEXADOR=COALESCE(A.INDEXADOR,B.INDEXADOR)
from VW_POSICAO A
LEFT JOIN TB_CAD_RF B on A.NM_ATIVO = B.CODIGO
LEFT JOIN TB_CAD_RF_2 D ON A.NM_ATIVO =D.CODIGO
Case when ISNULL((select * from table1) , (select * from table2) ) else select...
ISNULL is like an IIF statement, but if query field returns a null value, it tries the alternate query or you can set an alternate value.
Syntax may be a bit off, haven't written this query in a while, but it should put you on the right path. Google sql ISNULL

Execute a select from two tables with same columns

I have two tables with same columns, I need to make a select in this two tables, I want to know how is the best way to make this, my select test is:
SELECT
ISNULL(LoteDet.IdLoteDet, LoteDetPg.IdLoteDet) AS Expr1,
ISNULL(LoteDet.IDSac, LoteDetPg.IDSac) AS Expr2,
ISNULL(LoteDet.Comprom, LoteDetPg.Comprom) AS Expr3,
ISNULL(LoteDet.NossoNum, LoteDetPg.NossoNum) AS Expr4,
ISNULL(LoteDet.NossoNumDig, LoteDetPg.NossoNumDig) AS Expr5
FROM
LoteDet
CROSS JOIN
LoteDetPg
WHERE
Expr1 = 500
It's possible to make this ?
How is the better way to execute this kind of select, if not found the value in one table, the value will be in the other table....
------ EDIT
Perhaps create a view is a good alternative to this type of select?
Use COALESCE:
SELECT
COALESCE(LoteDet.IdLoteDet, LoteDetPg.IdLoteDet) AS Expr1,
COALESCE(LoteDet.IDSac, LoteDetPg.IDSac) AS Expr2,
COALESCE(LoteDet.Comprom, LoteDetPg.Comprom) AS Expr3,
COALESCE(LoteDet.NossoNum, LoteDetPg.NossoNum) AS Expr4,
COALESCE(LoteDet.NossoNumDig, LoteDetPg.NossoNumDig) AS Expr5
FROM
LoteDet
CROSS JOIN
LoteDetPg
WHERE
Expr1 = 500
Take a look on this documentation: https://msdn.microsoft.com/pt-br/library/ms190349.aspx
I believe this is going to return you what's called a Cartesian Product. It's the result of an open join, like you have above. That query is going to return TONS of records because you're not specifying how to JOIN the two tables, it's just going to blindly try matching columns. At the very least, add an ON condition to the JOIN so that you can match on IDs/keys. I think what you want is an INNER JOIN with an ON; this will return you all of the matching rows, based on ID/Key.
SELECT
CASE WHEN tbl1.Comprom IS NULL THEN tbl2.Comprom ELSE tbl1.Comprom END AS Expr1
CASE WHEN tbl1.Nossonum IS NULL THEN tbl2.Nossonum ELSE tbl1.Nossonum END AS Expr2
FROM
tbl1 --LoteDet
INNER JOIN tbl2 --LoteDetPg
ON (tbl1.ID = tbl2.ID)
WHERE
Expr1 = 500 --I know I swapped the expression values, use whichever expression you need here
Now, only rows that have a matching ID will return you values and it will use the value from tbl1, unless it is null, then it will use the value from tbl2.
Edit: I know CROSS JOIN turns into an INNER JOIN if a WHERE is specified, but does the WHERE need to include both tables? I feel that the Expr1 = 500 will still produce a Cartesian Product; can someone correct me?

the select query based on inner join doesn´t work, returns nothing

I have the code below that does not return any values when it should. Is there anyone that can find anything faulty with it? When I run the script I get 0 values in return (0 rows affected), even though I know that there are rows that should be selected.
This is just a part of the whole script, and when I run everything I get the error message "Warning: Null value is eliminated by an aggregate or other SET operation". But I don´t know what this implies for my script. I only have 1 "group by" in the script (which I don´t post here since that coding works). Does anyone know?
The code, below, should create a temp-table and to the temp-table insert rows from the table "StatusHistorik" where the following conditions are met:
1) [NyStatus]=4 (NyStatus is a column from the table "StatusHistorik"),
2) The variable "EnhetsId" should not exist in the table "SKL_AdminKontroll_SkaÅtgärdas" (F). Code: "F.EnhetsId is null".
I have used inner joins in both cases. This should result in a number of observations/rows in the temp-table, but returns nothing. Does anyone find any errors with the coding that could explain the absent of result?
I should mention that when I "comment away" the last inner join and the last part of the where-statement, the script works as it is supposed to. So I suspect that it is the last inner join-statement that is wrong somehow.
declare #temp2 table (
EnhetsId varchar(50),
TjanstId Int,
Tabell varchar(50),
Kommentar ntext,
Uppdaterad datetime
);
WITH ENHET_AVSLUT AS
(
SELECT DISTINCT A.[EnhetsId]
FROM [StatistikinlamningDataSKL].[dbo].[StatusHistorik] A
inner join (
select [EnhetsId], max(SenastUppdaterad) as SenastDatum
from [StatistikinlamningDataSKL].[dbo].[StatusHistorik]
group by [EnhetsId]
) B
on A.[EnhetsId] = B.[EnhetsId] and A.[SenastUppdaterad] = B.SenastDatum
INNER JOIN
StatistikinlamningDataSKL.dbo.SKL_AdminKontroll_SkaÅtgärdas F ON A.EnhetsId = F.EnhetsId
WHERE [NyStatus] = 4 AND F.EnhetsId is null
)
insert into #temp2
(EnhetsId, TjanstId, Tabell, Kommentar, Uppdaterad)
SELECT
EnhetsId, 1, ''GR_PS09_1'', ''OK'', getdate()
from ENHET_AVSLUT
select * from #temp2
Best regards,
Hannes
Because you are saying
The variable "EnhetsId" should not exist in the table "SKL_AdminKontroll_SkaÅtgärdas" (F). Code: "F.EnhetsId is null".
an INNER JOIN will try to join the tables and will not be able to perform a JOIN on NULL values, so the result will be zero rows.
So you must try using LEFT JOIN, where the tables ON LEFT will be pulled irrespective of the JOIN condition being satisfied.
I must warn you though, if the table is large it might have performance issues.
Your inner SQL must be something like
SELECT DISTINCT A.[EnhetsId]
FROM [StatistikinlamningDataSKL].[dbo].[StatusHistorik] A
inner join (
select [EnhetsId], max(SenastUppdaterad) as SenastDatum
from [StatistikinlamningDataSKL].[dbo].[StatusHistorik]
group by [EnhetsId]
) B
on A.[EnhetsId] = B.[EnhetsId] and A.[SenastUppdaterad] = B.SenastDatum
LEFT JOIN
StatistikinlamningDataSKL.dbo.SKL_AdminKontroll_SkaÅtgärdas F ON A.EnhetsId = F.EnhetsId
WHERE [NyStatus] = 4

Left Join With Where Clause

I need to retrieve all default settings from the settings table but also grab the character setting if exists for x character.
But this query is only retrieving those settings where character is = 1, not the default settings if the user havent setted anyone.
SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT JOIN `character_settings`
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1'
So i should need something like this:
array(
'0' => array('somekey' => 'keyname', 'value' => 'thevalue'),
'1' => array('somekey2' => 'keyname2'),
'2' => array('somekey3' => 'keyname3')
)
Where key 1 and 2 are the default values when key 0 contains the default value with the character value.
The where clause is filtering away rows where the left join doesn't succeed. Move it to the join:
SELECT `settings`.*, `character_settings`.`value`
FROM `settings`
LEFT JOIN
`character_settings`
ON `character_settings`.`setting_id` = `settings`.`id`
AND `character_settings`.`character_id` = '1'
When making OUTER JOINs (ANSI-89 or ANSI-92), filtration location matters because criteria specified in the ON clause is applied before the JOIN is made. Criteria against an OUTER JOINed table provided in the WHERE clause is applied after the JOIN is made. This can produce very different result sets. In comparison, it doesn't matter for INNER JOINs if the criteria is provided in the ON or WHERE clauses -- the result will be the same.
SELECT s.*,
cs.`value`
FROM SETTINGS s
LEFT JOIN CHARACTER_SETTINGS cs ON cs.setting_id = s.id
AND cs.character_id = 1
If I understand your question correctly you want records from the settings database if they don't have a join accross to the character_settings table or if that joined record has character_id = 1.
You should therefore do
SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT OUTER JOIN `character_settings`
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1' OR
`character_settings`.character_id is NULL
You might find it easier to understand by using a simple subquery
SELECT `settings`.*, (
SELECT `value` FROM `character_settings`
WHERE `character_settings`.`setting_id` = `settings`.`id`
AND `character_settings`.`character_id` = '1') AS cv_value
FROM `settings`
The subquery is allowed to return null, so you don't have to worry about JOIN/WHERE in the main query.
Sometimes, this works faster in MySQL, but compare it against the LEFT JOIN form to see what works best for you.
SELECT s.*, c.value
FROM settings s
LEFT JOIN character_settings c ON c.setting_id = s.id AND c.character_id = '1'
For this problem, as for many others involving non-trivial left joins such as left-joining on inner-joined tables, I find it convenient and somewhat more readable to split the query with a with clause. In your example,
with settings_for_char as (
select setting_id, value from character_settings where character_id = 1
)
select
settings.*,
settings_for_char.value
from
settings
left join settings_for_char on settings_for_char.setting_id = settings.id;
The way I finally understand the top answer is realising (following the Order Of Execution of the SQL query ) that the WHERE clause is applied to the joined table thereby filtering out rows that do not satisfy the WHERE condition from the joined (or output) table. However, moving the WHERE condition to the ON clause applies it to the individual tables prior to joining. This enables the left join to retain rows from the left table even though some column entries of those rows (entries from the right tables) do not satisfy the WHERE condition.
The result is correct based on the SQL statement. Left join returns all values from the right table, and only matching values from the left table.
ID and NAME columns are from the right side table, so are returned.
Score is from the left table, and 30 is returned, as this value relates to Name "Flow". The other Names are NULL as they do not relate to Name "Flow".
The below would return the result you were expecting:
SELECT a.*, b.Score
FROM #Table1 a
LEFT JOIN #Table2 b
ON a.ID = b.T1_ID
WHERE 1=1
AND a.Name = 'Flow'
The SQL applies a filter on the right hand table.