How to insert data into table with case expression? [duplicate] - sql

I have a query like this:
SELECT
jobs.*,
(
CASE
WHEN lead_informations.state IS NOT NULL THEN lead_informations.state
ELSE 'NEW'
END
) AS lead_state
FROM
jobs
LEFT JOIN lead_informations ON
lead_informations.job_id = jobs.id
AND
lead_informations.mechanic_id = 3
WHERE
lead_state = 'NEW'
Which gives the following error:
PGError: ERROR: column "lead_state" does not exist
LINE 1: ...s.id AND lead_informations.mechanic_id = 3 WHERE (lead_state...
In MySql this is valid, but apparently not in Postgresql. From what I can gather, the reason is that the SELECT part of the query is evaluated later than the WHERE part. Is there a common workaround for this problem?

I struggled on the same issue and "mysql syntax is non-standard" is not a valid argument in my opinion. PostgreSQL adds handy non-standard extensions as well, for example "INSERT ... RETURNING ..." to get auto ids after inserts. Also, repeating large queries is not an elegant solution.
However, I found the WITH statement very helpful (CTE's). It sort of creates a temporary view within the query which you can use like a usual table then. I'm not sure if I have rewritten your JOIN correctly, but in general it should work like this:
WITH jobs_refined AS (
SELECT
jobs.*,
(SELECT CASE WHEN lead_informations.state IS NOT NULL THEN lead_informations.state ELSE 'NEW' END) AS lead_state
FROM jobs
LEFT JOIN lead_informations
ON lead_informations.job_id = jobs.id
AND lead_informations.mechanic_id = 3
)
SELECT *
FROM jobs_refined
WHERE lead_state = 'NEW'

You would need to either duplicate the case statement in the where clause, or my preference is to do something like the following:
SELECT *
FROM (
SELECT
jobs.*,
(CASE WHEN lead_informations.state IS NOT NULL THEN lead_informations.state ELSE 'NEW' END) as lead_state
FROM
"jobs"
LEFT JOIN lead_informations ON lead_informations.job_id = jobs.id
AND lead_informations.mechanic_id = 3
) q1
WHERE (lead_state = 'NEW')

MySQL's support is, as you experienced, non-standard. The correct way is to reprint the same expression used in the SELECT clause:
SELECT
jobs.*,
CASE
WHEN lead_informations.state IS NOT NULL THEN lead_informations.state
ELSE 'NEW'
END AS lead_state
FROM
jobs
LEFT JOIN lead_informations ON
lead_informations.job_id = jobs.id
AND
lead_informations.mechanic_id = 3
WHERE
lead_informations.state IS NULL

I believe the common solution is to use an inner SELECT for the calculation (or CASE statement in this case) so that the result of the inner SELECT is available to the entire outer query by the time the execution gets to that query. Otherwise, the WHERE clause is evaluated first and knows nothing about the SELECT clause.

Subquery:
SELECT "tab_1"."BirthDate", "tab_1"."col_1" FROM (
SELECT BirthDate, DATEADD(year, 18, BirthDate) AS "col_1" FROM Employees
) AS "tab_1"
WHERE "tab_1"."col_1" >= '2000-12-31';

I used alias in where like this. (Sub Query).
Select "Vendors"."VendorId", "Vendors"."Name","Result"."Total"
From (Select "Trans"."VendorId", ("Trans"."A"+"Trans"."B"+"Trans"."C") AS "Total"
FROM "Trans"
WHERE "Trans"."Year"=2014
) As "Result"
JOIN "Vendors" ON "Result"."VendorId"="Vendors"."VendorId"
WHERE "Vendors"."Class"='I' AND "Result"."Total" > 200

Related

(probably) very simple SQL query needed

Having a slow day....could use some assistance writing a simple ANSI SQL query.
I have a list of individuals within families (first and last names), and a second table which lists a subset of those individuals. I would like to create a third table which flags every individual within a family if ANY of the individuals are not listed in the second table. The goal is essentially to flag "incomplete" families.
Below is an example of the two input tables, and the desired third table.
As I said...very simple...having a slow day. Thanks!
I think you want a left join and case expression:
select t1.*,
(case when t2.first_name is null then 'INCOMPLETE' else 'OK' end) as flag
from table1 t1 left join
table2 t2
on t1.first_name = t2.first_name and t1.last_name = t2.last_name;
Of course, this marks "Diane Thomson" as "OK", but I think that is an error in the question.
EDIT:
Oh, I see. The last name defines the family (that seems like a pretty big assumption). But you can do this with window functions:
select t1.*,
(case when count(t2.first_name) over (partition by t1.last_name) =
count(*) over (partition by t1.last_name)
then 'OK'
else 'INCOMPLETE'
end) as flag
from table1 t1 left join
table2 t2
on t1.first_name = t2.first_name and t1.last_name = t2.last_name;
That's not simple, at least not in SAS :-)
Standard SQL, when Windowed Aggregates are supported:
select ft.*,
-- counts differ when st.first_name is null due to the outer join
case when count(*) over (partition by ft.last_name)
= count(st.first_name) over (partition by ft.last_name)
then 'OK'
else 'INCOMPLETE'
end
from first_table as ft
left join second_table as st
on ft.first_name = st.first_name
and ft.last_name = ft.last_name
Otherwise you need to a standard aggregate and join back:
select ft.*, st.flag
from first_table as ft
join
(
select ft.last_name,
case when count(*)
= count(st.first_name)
then 'OK'
else 'INCOMPLETE'
end as flag
from first_table as ft
left join second_table as st
on ft.first_name = st.first_name
and ft.last_name = st.last_name
group by ft.last_name
) as st
on ft.last_name = st.last_name
It is pretty easy to do in SAS if you want to take advantage of its non-ANSI SQL feature of automatically re-merging aggregate function results back onto detail records.
select
a.first
, a.last
, case when 1=max(missing(b.last)) then 'INCOMPLETE'
else 'OK'
end as flag
from table1 a left join table2 b
on a.last=b.last and a.first=b.first
group by 2
order by 2,1
;

Using select case on a left join?

I have used a left join on two of my tables. Now I want to use case to identify the records from my left table who don't have a match in the right table. Such records exist and have a null value in the 'id_zeus' column of my join, however when I execute the case, it is as these fields don't exist. Where am I going wrong ? I get "Present" in all my column Disturbance. I am using Oracle SQL developer.
SELECT
CASE DP.ID_PRB
WHEN NULL
THEN 'Absence'
ELSE 'Present' END as Disturbance,
FROM
FIRE.WSITE WI
LEFT JOIN
(SELECT DISTINCT
DPL.ID_PERT as ID_PRB
FROM FIRE.DEPPLAN DPL
GROUP BY DPL.ID_PERT
) DPL
ON WI.ID_PERT = DP.ID_PERT
What is const? You don't seem to need it. The SELECT DISTINCT and GROUP BY are redundant, so use only one of them. And your alias on the subquery is incorrect.
But your problem is the comparison to NULL. It doesn't even match when doing a comparison as you are doing in CASE. You need to use IS NULL:
SELECT (CASE WHEN DP.ID_PRB IS NULL THEN 'Absence' ELSE 'Present'
END) as Disturbance,
FROM FIRE.WSITE WI LEFT JOIN
(SELECT DISTINCT DPL.ID_PERT as ID_PRB
FROM FIRE.OSI_DEVIATION_PLANS DP
) DP
ON WI.ID_PERT = DP.ID_PERT;
This query would commonly be written as:
SELECT (CASE WHEN NOT EXISTS (SELECT 1
FROM FIRE.OSI_DEVIATION_PLANS DP
WHERE WI.ID_PERT = DP.ID_PERT
)
THEN 'Absence' ELSE 'Present'
END) as Disturbance,
FROM FIRE.WSITE WI ;
This offers more opportunities for optimization.

teradata SQL tuning pundits - SELECT Failed. 3771: Illegal expression in WHEN clause of CASE expression

I am using a statement as below and get this error:
SELECT Failed. 3771: Illegal expression in WHEN clause of CASE
expression.
I had better hopes from Teradata. SQL Server can do it but Teradata can't.
How can I work around this? Any solution?
sel ( CASE
WHEN EXISTS ( sel '1' from VolatileTable Dtb1 where Dtb1.c1=FACT_Table_5MillionRows.C1)
THEN "FACTTablew5MillionRows"."CustomColumName"
ELSE 'ALL OTHER'
END ) (NAMED "CustomColumName" )
from
"Db"."FACTTablew5MillionRows"
Replace
WHEN EXISTS (...)
By
WHEN 1 = (SELECT 1 WHERE EXISTS (...))
Teradata doesn't like EXISTS in Correlated Scalar Subqueries within a CASE, but you can rewrite it like this:
select
( CASE
WHEN C1 = ( select MIN(C1) from VolatileTable Dtb1
where Dtb1.c1=ft.C1)
THEN ft."CustomColumName"
ELSE 'ALL OTHER'
END ) (NAMED "CustomColumName" )
from
"Db"."FACTTablew5MillionRows" as ft
If VolatileTable.C1 is unique you can remove the MIN.
But in 95% logic like this can be replaced by a LEFT JOIN:
select
ft.*,
CASE WHEN Dtb1.c1 IS NOT NULL
THEN ft."CustomColumName"
ELSE 'ALL OTHER'
end as "CustomColumName"
from "Db"."FACTTablew5MillionRows" as ft
left join VolatileTable Dtb1
on Dtb1.c1=ft.C1
This will return duplicated rows if VolatileTable.C1 is not unique, then you need to change it to:
from "Db"."FACTTablew5MillionRows" as ft
left join (select distinct C1 from VolatileTable) Dtb1
on Dtb1.c1=ft.C1
WHEN EXISTS (select '1' from VolatileTable Dtb1
where Dtb1.c1=FACT_Table_5MillionRows.C1)
THEN somevalue --or a statement that yields a scalar value
You were selecting a column in the then part, where you should be assigning a unique value.

Assign a value to a variable in a select statement and then used to find value in nested select statement

I'm getting the error
A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.
Here's my SELECT statement.
SELECT A.vendor_id, **#vendor_employee** = A.vendor_employee_id
, B.txt_first_name, B.txt_last_name,
SELECT txt_Vendor_Employee_Detail_Element,
(CASE
WHEN txt_Vendor_Employee_Detail_Value <> ''
AND txt_Vendor_Employee_Detail_Value IS NOT NULL
THEN txt_Vendor_Employee_Detail_Value
ELSE CONVERT(VARCHAR, txt_Vendor_Employee_Detail_Date)
END) AS Vendor_Detail_Element_Value
FROM t_vendor_employee_detail
WHERE vendor_employee_id = **#vendor_employee**)
FROM...
Yes, basically you can't return data and assign variables in the same select statement. What I think you intend to do is a correlated subquery which references an outer value.
That wolud look something like this:
SELECT A.vendor_id, A.vendor_employee_id
, B.txt_first_name, B.txt_last_name,
(SELECT d.txt_Vendor_Employee_Detail_Element
FROM t_vendor_employee_detail d
WHERE d.vendor_employee_id = A.Vendor_employee_id /* references outside the subqquery */)
FROM...
but you are also returning multiple rows in your subquery which should probably be rewritten as a join.
SELECT A.vendor_id, A.vendor_employee_id
, B.txt_first_name, B.txt_last_name,
d.txt_Vendor_Employee_Detail_Element,
(CASE
WHEN D.txt_Vendor_Employee_Detail_Value <> ''
AND d.txt_Vendor_Employee_Detail_Value IS NOT NULL
THEN d.txt_Vendor_Employee_Detail_Value
ELSE CONVERT(VARCHAR, d.txt_Vendor_Employee_Detail_Date)
END) AS Vendor_Detail_Element_Value
FROM vendor_table_A A
INNER JOIN t_vendor_employee_detail d
ON d.vendor_employee_id = A.vendor_employee_id
INNER JOIN vendor_table_B B
ON...
These examples will give you the basic idea but we would really need the whole query to give you a complete solution.
This looks like something that could be rewritten using a JOIN. It's hard to say how without seeing the whole query, but here is an attempt:
SELECT A.vendor_id, A.vendor_employee_id
, B.txt_first_name, B.txt_last_name,
c.txt_Vendor_Employee_Detail_Element,
(CASE
WHEN c.txt_Vendor_Employee_Detail_Value <> ''
AND c.txt_Vendor_Employee_Detail_Value IS NOT NULL
THEN c.txt_Vendor_Employee_Detail_Value
ELSE CONVERT(VARCHAR, c.txt_Vendor_Employee_Detail_Date)
END) AS Vendor_Detail_Element_Value
FROM ...
INNER JOIN t_vendor_employee_detail c
ON c.vendor_employee_id = A.vendor_employee_id
As the error message says, you cannot use a variable in the way you are trying.
You can retrieve and assign data altogether using a merge statement.
Something like this:
DECLARE #ID TABLE (
ID INT
)
MERGE INTO #ID
USING (
select 1 a,2 b,3 c
) as src
ON ( 1 = 2 )
WHEN NOT MATCHED THEN
INSERT (ID)
VALUES (Src.a)
OUTPUT src.b, src.c
;
SELECT * FROM #ID

Using an Alias column in the where clause in Postgresql

I have a query like this:
SELECT
jobs.*,
(
CASE
WHEN lead_informations.state IS NOT NULL THEN lead_informations.state
ELSE 'NEW'
END
) AS lead_state
FROM
jobs
LEFT JOIN lead_informations ON
lead_informations.job_id = jobs.id
AND
lead_informations.mechanic_id = 3
WHERE
lead_state = 'NEW'
Which gives the following error:
PGError: ERROR: column "lead_state" does not exist
LINE 1: ...s.id AND lead_informations.mechanic_id = 3 WHERE (lead_state...
In MySql this is valid, but apparently not in Postgresql. From what I can gather, the reason is that the SELECT part of the query is evaluated later than the WHERE part. Is there a common workaround for this problem?
I struggled on the same issue and "mysql syntax is non-standard" is not a valid argument in my opinion. PostgreSQL adds handy non-standard extensions as well, for example "INSERT ... RETURNING ..." to get auto ids after inserts. Also, repeating large queries is not an elegant solution.
However, I found the WITH statement very helpful (CTE's). It sort of creates a temporary view within the query which you can use like a usual table then. I'm not sure if I have rewritten your JOIN correctly, but in general it should work like this:
WITH jobs_refined AS (
SELECT
jobs.*,
(SELECT CASE WHEN lead_informations.state IS NOT NULL THEN lead_informations.state ELSE 'NEW' END) AS lead_state
FROM jobs
LEFT JOIN lead_informations
ON lead_informations.job_id = jobs.id
AND lead_informations.mechanic_id = 3
)
SELECT *
FROM jobs_refined
WHERE lead_state = 'NEW'
You would need to either duplicate the case statement in the where clause, or my preference is to do something like the following:
SELECT *
FROM (
SELECT
jobs.*,
(CASE WHEN lead_informations.state IS NOT NULL THEN lead_informations.state ELSE 'NEW' END) as lead_state
FROM
"jobs"
LEFT JOIN lead_informations ON lead_informations.job_id = jobs.id
AND lead_informations.mechanic_id = 3
) q1
WHERE (lead_state = 'NEW')
MySQL's support is, as you experienced, non-standard. The correct way is to reprint the same expression used in the SELECT clause:
SELECT
jobs.*,
CASE
WHEN lead_informations.state IS NOT NULL THEN lead_informations.state
ELSE 'NEW'
END AS lead_state
FROM
jobs
LEFT JOIN lead_informations ON
lead_informations.job_id = jobs.id
AND
lead_informations.mechanic_id = 3
WHERE
lead_informations.state IS NULL
I believe the common solution is to use an inner SELECT for the calculation (or CASE statement in this case) so that the result of the inner SELECT is available to the entire outer query by the time the execution gets to that query. Otherwise, the WHERE clause is evaluated first and knows nothing about the SELECT clause.
Subquery:
SELECT "tab_1"."BirthDate", "tab_1"."col_1" FROM (
SELECT BirthDate, DATEADD(year, 18, BirthDate) AS "col_1" FROM Employees
) AS "tab_1"
WHERE "tab_1"."col_1" >= '2000-12-31';
I used alias in where like this. (Sub Query).
Select "Vendors"."VendorId", "Vendors"."Name","Result"."Total"
From (Select "Trans"."VendorId", ("Trans"."A"+"Trans"."B"+"Trans"."C") AS "Total"
FROM "Trans"
WHERE "Trans"."Year"=2014
) As "Result"
JOIN "Vendors" ON "Result"."VendorId"="Vendors"."VendorId"
WHERE "Vendors"."Class"='I' AND "Result"."Total" > 200