SQL Query, ELSE LAG argument to pull value above in same row - sql

I am hoping someone will be able to help I have the below query (Query1) that works fine. but I want to change the line
CASE WHEN pegging_level_no = 0 THEN (supply_order_num) ELSE 'TEST' end AS Assembly_WO
So that rather than returning 'TEST' if pegging_level_no = 0 it returns the Value from the row above in this new column, I am not sure this is possible? (I am new to SQL, and learning as I go)
Basically, I am trying to recreate the formula if it was in excel similar to =IF(D2=0,O2,AF1) this would be in cell AF2.
I have tried using the LAG Function but can not get it to work below is what I have been trying to use but does not work, tried adding a group by but struggling!
CASE WHEN pegging_level_no = 0 THEN (supply_order_num) ELSE LAG(Assembly_WO) OVER(PARTITION BY pegging_level_no) END AS Assembly_WO
Query1
SELECT
plant_sys,
so_tran_order_number,
so_tran_line_number,
pegging_level_no,
demand_order_number,
supply_type_desc,
supply_material,
allocated_qty,
ext_cost,
pegging_seq_2,
Sum(allocated_qty)OVER (partition BY plant_sys, supply_material) AS Total_Qty,
CASE WHEN est_cost <> 0 and supply_type_desc ='Production order - PO' THEN (est_cost/allocated_qty)end AS WoPO_cost_Ea,
-- CASE WHEN pegging_level_no = 0 THEN (supply_order_num) ELSE LAG(Assembly_WO) OVER(PARTITION BY pegging_level_no) END AS Assembly_WO
CASE WHEN pegging_level_no = 0 THEN (supply_order_num) ELSE 'TEST' end AS Assembly_WO
FROM
og_surf_snop_dm.sop_plan_calc_cp_t
WHERE PRODUCT_COMPANY = 'OFS' AND PLANT_SYS = '0003'
ORDER BY
pegging_seq_2 ASC,
pegging_level_no ASC;
Sample Data(so where it says 'TEST' in the last column I would like it to return the value from the cell above in column heading 'ASSEMBLY_WO:

Related

SQL - CASE WHEN result outputting conflicting results

I'm working to solve
https://platform.stratascratch.com/coding/10065-find-whether-the-number-of-seniors-works-at-facebook-is-higher-than-its-number-of-usa-based-employees?python=
This is the query I've attempted to write:
SELECT CASE WHEN COUNT(CASE WHEN location = 'US' THEN 1 ELSE 0 END) >
COUNT(CASE WHEN is_senior = true THEN 1 ELSE 0 END) THEN 'More USA-based'
ELSE 'More seniors' END AS what_do_we_have_more_of
FROM facebook_employees
Result: 'More seniors'
However, when I rewrite it with the conditions flipped around:
SELECT CASE WHEN COUNT(CASE WHEN is_senior = true THEN 1 ELSE 0 END) >
COUNT(CASE WHEN location = 'US' THEN 1 ELSE 0 END) THEN 'More seniors'
ELSE 'More USA-based' END AS what_do_we_have_more_of
FROM facebook_employees
Result: 'More USA-based'
Can someone please explain why there is a discrepancy here? What is wrong with the query I've written?
I know this problem can be solved with sub-queries but I wanted to try out a CASE WHEN approach specifically. Is this more efficient?
Edit: the solution I wrote with sub-queries (works with conditions reversed)
WITH us_employees AS (
SELECT id, location
FROM facebook_employees
WHERE location = 'US'
),
senior_employees AS (
SELECT id, is_senior
FROM facebook_employees
WHERE is_senior = true
)
SELECT CASE WHEN COUNT(location) < COUNT(is_senior) THEN 'More seniors' ELSE 'More US-based' END AS what_do_we_have_more_of
FROM us_employees u
FULL JOIN senior_employees s
ON u.id = s.id
Result: 'More seniors'
The use of count() in your query is incorrect. But how do you get different results? Because both counts are the same. So A > B is always false, and you always end up in the ELSE branch.
A proper query could look like this:
SELECT CASE WHEN count(*) FILTER (WHERE location = 'US')
> count(*) FILTER (WHERE is_senior) THEN 'More USA-based'
WHEN count(*) FILTER (WHERE location = 'US')
< count(*) FILTER (WHERE is_senior) THEN 'More seniors'
ELSE 'US-based and seniors tie' END AS what_do_we_have_more_of
FROM facebook_employees;
See:
Aggregate columns with additional (distinct) filters
Note, this can never fail with NULL values, because count() (unlike most aggregate functions) never returns NULL.
You should use SUM instead of COUNT.
COUNT will count +1 even when your CASE return 1 or 0.
SUM only count +1 when your CASE return 1.
So assume that your table has 1000 rows, then both your two queries will be CASE 1000 > 1000 THEN ... ELSE... END.

SQL - Subselect in select clause - how to create column which decides uniqity logic

I am trying to write subselect which will run through returned data, then checks status of all and then decides uniquity logic.
Is there any way to find out following ?
case any of data has 'Active' status first one will be marked as 1 everything else as 0
case there is no 'Active' status then first 'Expired' status will by marked as 1 and everything else as 0
case there is no 'Active' and 'Expired' status then first 'In Progress' will be marked as 1 and everything else as 0
I was trying to write it like this but i need to have it in one case statement
SELECT a.id, a.status,
,(SELECT
CASE WHEN b.STATUS = 'Active' THEN 1 ELSE 0 END
CASE WHEN b.STATUS = 'Expired' THEN 1 ELSE 0 END
FROM b.TABLE
WHERE a.id=b.id )AS unique
FROM my.TABLE
Result should look like https://i.stack.imgur.com/qCA74.png picture for expired case
Thank you in advance for any tips.
Use a window function:
select t.*,
(case when row_number() over (partition by id
order by case status when 'Active' then 1 when 'Expired' then 2 else 3 end
) = 1
then 1 else 0
end) as unique_flag
from my.table t;
If the lookup table is the same as source table, then you can use LAG function with constant and use its default value to mark the first row with 1 and others with 0. But you need to order your rows by some fields to deal with duplicates on status.
select a.id, a.status,
lag(0, 1, 1) over(
partition by a.id
order by
case a.status
when 'Active' then 0
when 'Expired' then 1
else 3
end asc,
a.some_more_columns asc /*To find that first row when there are duplicates by status*/
) as unique_flag
from MY_TABLE a
And what about object naming: never use keywords as identifiers. Calling column with date as date, table with users as users and some unknown table as table makes you design error prone.

SQL Order By blanks at bottom when the main order by is a Case statement?

I'd like to ORDER BY listing items that are blanks at the bottom of the result, however the main order by is a case statement.
So, my main Order By clause is this:
ORDER BY CASE WHEN pub = 1 THEN title ELSE t.othertitle END;
To that, I'd like to apply the following CASE so that blanks go to the bottom:
ORDER BY CASE WHEN (above case) = '' THEN 1 ELSE 0 END
Problem is I cannot embed CASE statements.
How would this be done?
Thanks!
You didn't specify your DBMS, so this is ANSI SQL:
ORDER BY nullif(CASE WHEN pub = 1 THEN title ELSE t.othertitle END, '') NULLS LAST
You can in fact embed case statements
ORDER BY CASE COALESCE(CASE pub WHEN 1 THEN title ELSE t.othertitle END, '') WHEN '' THEN 1 ELSE 0 END
You can do this explicitly:
ORDER BY (CASE WHEN pub = 1 AND title = '' THEN 1
WHEN pub <> 1 AND t.othertitle = '' THEN 1
ELSE 2
END) DESC,
(CASE WHEN pub = 1 THEN title ELSE t.othertitle END)
Case statements can only(?) be included in Order by when structuring it to return an INT. Create any kind of ordering logic you'd like but make sure it returns an INT in ascending order:
order by
(case when pub = 1 then 1
when pub is null or pub = '' then 2
else 3 end)

Getting a sum of only the first instance of a column in a sql query

I have the following SQL code:
SELECT EmployeeID,
SUM(CASE
WHEN Error1 = '0'
THEN 1
ELSE 0
END + CASE
WHEN Error2 = '0'
THEN 1
ELSE 0
END + CASE
WHEN Error3 = '0'
THEN 1
ELSE 0
END) AS TotalErrors
FROM SubmittedDocuments
GROUP BY EmployeeID
The statement should calculate the number of errors in the table for each employee. However, there is another column in the table SubmittedDocuments named "DocumentName". How could I write a statement that only counts errors for the first instance of each DocumentName? (Or only counts for the one with the lowest "SubmittedID", the unique identifier)
Sorry if anything in unclear, I will attempt to clear up any confusion in the comments.
I might have not got this right from your question, but I think this should work. If you could show some sample data and expected output then we can definitely have a sure answer.
SELECT EmployeeID
SUM(CASE WHEN DocIDErr > 1 THEN 0 ELSE 1 END)
FROM
(SELECT EmployeeID
, Error1
, ROW_NUMBER() OVER (PARTITION BY EmployeeID ORDER BY DocumentName) AS DocIDErr
FROM SubmittedDocuments) AS RS
GROUP BY EmployeeID
I don't think you want aggregation. I think you just want to select the first document for each name.
If so, this may be what you want:
select sd.*,
((case when error1 = '0' then 1 else 0 end) +
(case when error2 = '0' then 1 else 0 end) +
(case when error3 = '0' then 1 else 0 end)
) as numerrors
from (select sd.*,
row_number() over (partition by documentname order by submittedid) as seqnum
from SumittedDocuments sd
) sd
where seqnum = 1;

How to do a SUM() inside a case statement in SQL server

I want to add some calculation inside my case statement to dynamically create the contents of a new column but I get the error:
Column 'Test1.qrank' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
This is the code I'm working on
case
when test1.TotalType = 'Average' then Test2.avgscore
when test1.TotalType = 'PercentOfTot' then (cnt/SUM(test1.qrank))
else cnt
end as displayscore
I did try to group but it didn't work.
Any hints?
The error you posted can happen when you're using a clause in the GROUP BY statement without including it in the select.
Example
This one works!
SELECT t.device,
SUM(case when transits.direction = 1 then 1 else 0 end) ,
SUM(case when transits.direction = 0 then 1 else 0 end) from t1 t
where t.device in ('A','B') group by t.device
This one not (omitted t.device from the select)
SELECT
SUM(case when transits.direction = 1 then 1 else 0 end) ,
SUM(case when transits.direction = 0 then 1 else 0 end) from t1 t
where t.device in ('A','B') group by t.device
This will produce your error complaining that I'm grouping for something that is not included in the select
Please, provide all the query to get more support.
You could use a Common Table Expression to create the SUM first, join it to the table, and then use the WHEN to to get the value from the CTE or the original table as necessary.
WITH PercentageOfTotal (Id, Percentage)
AS
(
SELECT Id, (cnt / SUM(AreaId)) FROM dbo.MyTable GROUP BY Id
)
SELECT
CASE
WHEN o.TotalType = 'Average' THEN r.avgscore
WHEN o.TotalType = 'PercentOfTot' THEN pt.Percentage
ELSE o.cnt
END AS [displayscore]
FROM PercentageOfTotal pt
JOIN dbo.MyTable t ON pt.Id = t.Id
If you're using SQL Server 2005 or above, you can use the windowing function SUM() OVER ().
case
when test1.TotalType = 'Average' then Test2.avgscore
when test1.TotalType = 'PercentOfTot' then (cnt/SUM(test1.qrank) over ())
else cnt
end as displayscore
But it'll be better if you show your full query to get context of what you actually need.