I trying to create a SQL query with a CASE WHEN EXISTS clause in SQL Server. I assume I am doing something wrong as when I run the SELECT * FROM [Christmas_Sale] it takes forever for SQL to load the code.
CREATE VIEW [Christmas_Sale]
AS
SELECT
C.*,
CASE
WHEN EXISTS (SELECT S.Sale_Date
FROM [Christmas_Sale] s
WHERE C.ID = S.ID)
THEN 0
ELSE 1
END AS ChristmasSale
FROM
[Customer_Detail] C ;
I'm trying to write a sub select which I need to return a 1 if Sale_Date= 1 and 0 for anything else.
The syntax of your query looks ok. But since your stated:
I'm trying to write a sub select which I need to return a 1 if Sale_Date= 1 and 0 for anything else.
... Then you could rephrase your query by adding one more condition in the WHERE clause of the subquery:
CREATE VIEW [Christmas_Sale]AS
SELECT
C.*,
CASE WHEN EXISTS (
SELECT 1
FROM [Christmas_Sale] s
WHERE C.ID = S.ID and S.Sale_Date = 1
) THEN 0 ELSE 1 END AS ChristmasSale
FROM [Customer_Detail] C ;
If a record exists in [Christmas_Sale] with the corresponding ID and Sale_Date = 1, then ChristmasSale will have value 1, else it will display 0.
This query looks correct:
CREATE VIEW [Christmas_Sale] AS
SELECT C.*,
(CASE WHEN EXISTS (SELECT 1
FROM [Christmas_Sale] s
WHERE C.ID = S.ID
)
THEN 0 ELSE 1
END) AS ChristmasSale
FROM [Customer_Detail] C ;
If performance is an issue, you want an index on Christmas_Sale(ID).
Note that the SELECT S.Sale_Date in the subquery is meaningless, because EXISTS checks for rows not columns. Hence, I replaced it with the simpler 1.
Related
I'm trying to understand how case when exists expression works, there is two tables one is trOrderHeader which stores main info about any order. Other is trOrderLine which stores details about an order and there is a column IsClosed which indicates Order is somehow closed (canceled or completed).
So my query is below which I am trying to get OrderLineID with IsClosed column (I can do it by joins it is easy but I am trying case when exists expression) but all IsClosed column returns 1;
SELECT
OrderLineId,
IsClosed =
CASE
WHEN EXISTS (select * from trOrderHeader where IsClosed=1)
THEN 1
WHEN EXISTS (select * from trOrderHeader where IsClosed=0)
THEN 0
END
FROM
trOrderLine
GROUP BY OrderLineId
Isn't this supposed to give me if an Order is Closed write 1, if an Order is not Closed write 0?
Did I misunderstand case when exists expression?
You would need to correlate the subqueries. Assuming that column OrderId can be used to relate the tables, then:
SELECT
OrderLineId,
IsClosed =
CASE
WHEN EXISTS (select 1 from trOrderHeader h where h.IsClosed = 1 AND h.OrderId = l.OrderId)
THEN 1
WHEN EXISTS (select 1 from trOrderHeader h where IsClosed = 0 AND h.OrderId = l.OrderId)
THEN 0
END
FROM trOrderLine l
GROUP BY OrderLineId
However, the logic of the query could probably be simplified by using a JOIN and an aggregate function to compute the status. Something like this could be what you need:
SELECT l.OrderLineId, MAX(h.isClosed) IsClosed
FROM trOrderLine l
INNER JOIN trOrderHeader h ON h.OrderId = l.OrderId
GROUP BY l.OrderLineId
Is the following problematic?
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
WHERE EXISTS
(
SELECT *
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1 AND
a.[Index] = b.[Index]
HAVING SUM(b.Amount) = 0
)
Reason I'm slightly uneasy about using the above script is that if I run the following it errors:
SELECT *
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1
HAVING SUM(b.Amount) = 0
I understand why this script errors => the select is not grouped on anything therefore the processor does not like the aggregation in the HAVING clause.
But as a sub-query this error does not occur - why? Is this a problematic approach?
EDIT
Ended up using the following:
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
WHERE a.[Index] IN
(
SELECT [Index]
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData
WHERE [Past28Days] = 1
GROUP BY [Index]
HAVING SUM(Amount) = 0
)
But as suggested in answer the following is more readable by simply adding the GROUP BY into the sub-query:
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
WHERE EXISTS
(
SELECT *
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1 AND
a.[Index] = b.[Index]
GROUP BY b.[Index]
HAVING SUM(b.Amount) = 0
)
It is legal to omit group by and still perform aggregations, therefore having is still a way of limiting results:
select sum(x)
from
(
select 1 x union all select 2
) a
having sum(x) = 3
Exists() work because everything in select list is ignored. Exists() looks for rows only, and terminates as soon as one is found. You might add group by b.Index to make intent clear to anyone reviewing the code later, or rewrite it using inner join and derived table.
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
INNER JOIN
(
SELECT b.[Index]
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1
GROUP BY b.[Index]
HAVING SUM(b.Amount) = 0
) b1
ON a.[Index] = b1.[Index]
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
I have an existing sql query that I'd like to apply to every record returned from a "distinct" query.
I guess something like looping through each of the returned records, storing it as a string, and using that value in the other query. How would I go about this?
sudo queries:
Select ...
for each record returned as X,
Select ... etc ... where ... LIKE X
Edit:
not sure how to make it clearer, but I know I'm probably not making it obvious. I'll try:
The distinct will return a single column, with many records. I need to apply each value to the second sql query.
So like.. Select X and Y, but Y is returned from the 2nd query I have, using X
Edit2:
If the distinct select returns
1
2
3
4
And the second query returns a single record "A" when the where clause looks like ... = '1', "B" when the where clause looks like ... = '2', "C" when the where clause looks like ... = '3', and C when the where clause looks like ... = '4'
Then I'd like my final output to look like
1 | A
2 | B
3 | C
4 | C
Edit 3:
first query:
SELECT DISTINCT [user_id] from dbo.sap_empl_subset
second query:
SELECT [name_pref_mixed]
FROM dbo.sap_empl_subset AS E
WHERE E.sap_position_no IN
(SELECT P.sap_position_no
FROM dbo.sap_position AS P
WHERE (LTRIM(RTRIM(P.sap_position_desc)) LIKE '%[VICE ]PRESIDENT%')
OR (LTRIM(RTRIM(P.sap_position_desc)) LIKE 'CHIEF%'))
AND E.sap_org_code =
(SELECT
CASE
WHEN S.sap_org_code_level2 = 0 THEN S.sap_org_code
WHEN S.sap_org_code_level3 = 0 THEN S.sap_org_code_level1
ELSE S.sap_org_code_level2
END
FROM dbo.sap_org_structure AS S
WHERE S.sap_org_code =
(SELECT E1.sap_org_code
FROM dbo.sap_empl_subset AS E1
WHERE E1.[user_id] = '<each item from first query needs applied here>'))
SELECT *
FROM (
SELECT DISTINCT value
FROM mytable
) x
JOIN othertable y
ON y.value LIKE '%' || x.value || '%'
Update:
If you first query is
SELECT my_x
FROM mytable
WHERE my_y = '…'
and the second one is
SELECT other_z
FROM othertable
WHERE other_y = my_x
the you just need a join:
SELECT my_x, other_z
FROM mytable
JOIN othertable
ON other_y = my_x
WHERE my_y = '…'
It would be much more easy to answer if you just posted the queries.
Update 2:
Try this:
SELECT es.user_id, esp.name_pref_mixed
FROM sap_empl_subset es
JOIN sap_org_structure os
ON os.sap_org_code = es.sap_org_code
JOIN sap_empl_subset esс
ON esc.sap_org_code =
CASE
WHEN os.sap_org_code_level2 = 0 THEN os.sap_org_code
WHEN os.sap_org_code_level3 = 0 THEN os.sap_org_code_level1
ELSE os.sap_org_code_level2
END
WHERE esc.sap_position_no IN
(
SELECT sap_position_no
FROM sap_position sp
WHERE (LTRIM(RTRIM(sp.sap_position_desc)) LIKE '%[VICE ]PRESIDENT%')
OR (LTRIM(RTRIM(sp.sap_position_desc)) LIKE 'CHIEF%'))
)
DISTINCT seems to be redundant here. You have a condition in your second query:
WHERE S.sap_org_code =
(
SELECT E1.sap_org_code
FROM dbo.sap_empl_subset AS E1
WHERE E1.[user_id] = '<each item from first query needs applied here>')
)
which would throw an error if there were duplicates on sap_empl_subset.user_id
A join was not necessary to combine the two queries. All I needed was the nested select syntax as shown below, where the first line is the first query, and the first nested select is the second query. A join was not necessary.
SELECT Distinct U.[user_id] AS "User ID", (
SELECT [empl_last_name]
FROM dbo.sap_empl_subset AS E
WHERE E.sap_position_no IN
(SELECT P.sap_position_no
FROM dbo.sap_position AS P
WHERE (LTRIM(RTRIM(P.sap_position_desc)) LIKE '%[VICE ]PRESIDENT%')
OR (LTRIM(RTRIM(P.sap_position_desc)) LIKE '%CHIEF%')
OR (LTRIM(RTRIM(P.sap_position_desc)) LIKE '%[EXECUTIVE ]VP%')
)
AND E.sap_org_code =
(SELECT
CASE
WHEN S.sap_org_code_level2 = 0 THEN S.sap_org_code
WHEN S.sap_org_code_level3 = 0 THEN S.sap_org_code_level1
ELSE S.sap_org_code_level2
END
FROM dbo.sap_org_structure AS S
WHERE S.sap_org_code =
(SELECT E1.sap_org_code
FROM dbo.user_id AS E1
WHERE E1.[user_id] = U.[user_id]))) As "VP"
From dbo.user_id As U WHERE U.[user_id] <> ''
ORDER BY [User ID]
I have a following query (simplified):
SELECT
Id
FROM
dbo.Entity
WHERE
1 = ALL (
SELECT
CASE
WHEN {Condition} THEN 1
ELSE 0
END
FROM
dbo.Related
INNER JOIN dbo.Entity AS TargetEntity ON
TargetEntity.Id = Related.TargetId
WHERE
Related.SourceId = Entity.Id
)
where {Condition} is a complex dynamic condition on TargetEntity.
In simple terms, this query should return entities for which all related entities match the required condition.
Unfortunately, that does not work quite well, since by SQL standard 1 = ALL evaluates to TRUE when ALL is applied to an empty set. I know I can add AND EXISTS, but that will require me to repeat the whole subquery, which, I am certain, will cause problems for performance.
How should I rewrite the query to achieve the result I need (SQL Server 2008)?
Thanks in advance.
Note: practically speaking, the whole query is highly dynamic, so the perfect solution would be to rewrite only 1 = ALL ( ... ), since changing top-level select can cause problems when additional conditions are added to top-level where.
Couldn't you use a min to achieve this?
EG:
SELECT
Id
FROM
dbo.Entity
WHERE
1 = (
SELECT
MIN(CASE
WHEN {Condition} THEN 1
ELSE 0
END)
FROM
dbo.Related
INNER JOIN dbo.Entity AS TargetEntity ON
TargetEntity.Id = Related.TargetId
WHERE
Related.SourceId = Entity.Id
)
The min should return null if there's no clauses, 1 if they're all 1 and 0 if there's any 0's, and comparing to 1 should only be true for 1.
It can be translated to pick Entities where no related entities with unmatched condition exist.
This can be accomplished by:
SELECT
Id
FROM
dbo.Entity
WHERE
NOT EXISTS (
//as far as I have an element which do not match the condition, skip this entity
SELECT TOP 1 1
FROM
dbo.Related
INNER JOIN dbo.Entity AS TargetEntity ON
TargetEntity.Id = Related.TargetId
WHERE
Related.SourceId = Entity.Id AND
CASE
WHEN {Condition} THEN 1
ELSE 0
END = 0
)
EDIT: depending on condition, you can write something like:
WHERE Related.SourceId = Entity.Id AND NOT {Condition} if it doesn't change too much the complexity of the query.
Instead of using all, change your query to compare the result of the subquery directly:
select Id
from dbo.Entity
where 1 = (
select
case
when ... then 1
else 0
end
from ...
where ...
)
Probably this will work: WHERE NOT 0 = ANY(...)
If I read the query correctly, it can be simplified to something like:
SELECT e.Id
FROM dbo.Entity e
INNER JOIN dbo.Related r ON r.SourceId = e.Id
INNER JOIN dbo.Entity te ON te.Id = r.TargetId
WHERE <extra where stuff>
GROUP BY e.Id
HAVING SUM(CASE WHEN {Condition} THEN 1 ELSE 0 END) = COUNT(*)
This says the Condition must be true for all rows. It filters the "empty" set case away with the INNER JOINs.