SQL IF type logic help requested - sql

I have a SQL problem that I have been stuck on for days. So this is the context. I work for a company where employees have timesheets. Each timesheet has an ID but it is not unique because it is possible for an employee to have 2 timesheets for the same ID. The difference is that normally when you submit the sheet your status is ‘Posted’. But, sometimes people screw up their entries and it has to get re-submitted with changes. Therefore, the status ‘Adjusted’.
The logic I need is the following
-Where timesheet ID’s only have one value (count=1) always use ‘Posted’ status. If there is only one value but it is not ‘Posted’ return an error string saying ‘Error’.
-where timesheet IDs have more than one value and BOTH ‘Posted’ and ‘Adjusted’ show up as status always default to ‘Adjusted’. BOTH posted and adjusted must be present in this.
I have tried case and subquery but no luck. I also have a column ‘timesheet post date’ and logic is earliest date is always posted and later date is ‘adjusted’, but in some cases the posting dates are identical.
so as you can see, I need to look at the duplicate count in one column, and then choose the value if that count is >1 from another column.
SELECT t1.[Resource NUID]
,t1.[Timesheet ID]
,t1.[Timesheet Start Date]
,t1.[Timesheet End Date]
,t1.[Timesheet Posted Date]
,t1.[Timesheet Status]
,t1.[RunSourceID]
,t1.[SpanStartDate]
,t1.[SpanEndDate]
FROM [TIME_DW].[dbo].[Timecard_Timesheets] as t1, [TIME_DW].[dbo]. [Timecard_Timesheets] as t2
where t1.[Timesheet ID]=t2.[Timesheet ID]
and t1.[Resource NUID]='e066308' and t1.[Timesheet Status]<>'Open' and t1.[Timesheet Status]<>'Submitted'
group by
t1.[Resource NUID]
,t1.[Timesheet ID]
,t1.[Timesheet Start Date]
,t1.[Timesheet End Date]
,t1.[Timesheet Posted Date]
,t1.[Timesheet Status]
,t1.[RunSourceID]
,t1.[SpanStartDate]
,t1.[SpanEndDate]
order by t1.[Timesheet Start Date] asc
this is an example of an actual record that has two statuses
thanks

I am expecting logic like this:
select timesheet_id,
(case when count(*) = 1 and min(status) = 'Posted' then min(status)
when count(*) = 1 then 'Error'
when min(status) = 'Adjusted' and max(status) = 'Posted' then 'Adjusted'
else NULL -- this case is not covered in the description
end) as new_status
from [TIME_DW].[dbo].[Timecard_Timesheets]
group by timesheet_id;
I don't understand what all the other columns are going in the code in the question.

This should get you going in the right direction:
First we count rows by TimeSheet Id (CN) and we assign a row_number (RN) ordered by "Adjusted" records first, then everything else (you might want to add an adjustment date as a 2nd order by to get the most recent one first).
Then we add an error status if the first and only row is not a Status of "Posted".
Finally we select out only the rows WHERE RN=1
DECLARE #TimeSheet TABLE (Id INT, Status VARCHAR(15))
INSERT INTO #TimeSheet (Id,Status)
VALUES
(1,'Posted'),
(2,'Posted'),
(2,'Adjusted'),
(3,'Adjusted')
;WITH X AS
(
SELECT COUNT(2) OVER(PARTITION BY Id) AS CN,
ROW_NUMBER() OVER(PARTITION BY Id ORDER BY CASE WHEN Status='Adjusted' THEN 0 ELSE 1 END) AS RN,
*
FROM #TimeSheet
), Y AS
(
SELECT CASE WHEN CN=1 AND RN=1 AND Status<>'Posted' THEN 'Error'
ELSE ''
END AS Error,
*
FROM X
)
SELECT *
FROM Y
WHERE RN=1

from your code, I think you were trying to do this :
SELECT
ts.[Resource NUID]
, ts.[Timesheet ID]
, ts.[Timesheet Start Date]
, ts.[Timesheet End Date]
, ts.[Timesheet Posted Date]
, ts.[Timesheet Status]
, ts.[RunSourceID]
, ts.[SpanStartDate]
, ts.[SpanEndDate]
FROM
[TIME_DW].[dbo].[Timecard_Timesheets] as ts
JOIN (
SELECT *
, CASE
WHEN TimeSheetCount > 1 AND [Timesheet Status] <> 'Posted' THEN 'Adjusted'
WHEN TimeSheetCount = 1 AND [Timesheet Status] <> 'Posted' THEN 'Error'
ELSE 'Posted'
END NewStatus
FROM (
SELECT *
, COUNT(*) OVER(PARTITION BY t1.[Timesheet ID]) TimeSheetCount
, ROW_NUMBER() OVER(ORDER BY t1.[Timesheet Start Date]) RN
FROM
[TIME_DW].[dbo].[Timecard_Timesheets] as t1
) D
) t2 ON ts.[Timesheet ID] = t2.[Timesheet ID]
WHERE
ts.[Resource NUID] = 'e066308'
AND ts.[Timesheet Status] <> 'Open'
AND ts.[Timesheet Status] <> 'Submitted'

Related

Getting 2 result columns from SQL query

I've tried to make an SQL query to get 2 result columns from 1 query. To elaborate:
This is my query:
SELECT Name, AVG(DATEDIFF(day,[Open Date],[Close Date]))
FROM Inventory Where Tested ='Yes' AND [Close Date] IS NOT NULL AND [Open Date] IS NOT NULL
GROUP BY Name
My result:
My desired result:
When also Where Tested = 'No'
How to write an SQL query with Where Tested = 'Yes' and Where Tested = 'No'? Where Tested = 'Yes' is Column 1 result and Tested = 'No' is Column 2 result.
Database:SQL server 2019
EJC answer with subqueries should already probably help with what you need, but just wanted to share some other options to achieve this:
Joining two separate queries
SELECT tested.Name, tested.TestedAVG, not_tested.NotTestedAVG FROM
(SELECT Name, AVG(DATEDIFF(day,[Open Date],[Close Date])) as "TestedAVG"
FROM Inventory
WHERE Tested ='Yes' AND [Close Date] IS NOT NULL AND [Open Date] IS NOT NULL
GROUP BY Name) tested
FULL JOIN
(SELECT Name, AVG(DATEDIFF(day,[Open Date],[Close Date])) as "NotTestedAVG"
FROM Inventory
WHERE Tested ='No' AND [Close Date] IS NOT NULL AND [Open Date] IS NOT NULL
GROUP BY Name) not_tested
ON tested.Name = not_tested.Name
Using case
SELECT Name,
AVG(CASE WHEN Tested ='Yes' THEN DATEDIFF(day,[Open Date],[Close Date]) END) as [Tested AVG],
AVG(CASE WHEN Tested ='No' THEN DATEDIFF(day,[Open Date],[Close Date]) END) as [Not Tested AVG]
FROM Inventory
WHERE [Close Date] IS NOT NULL AND [Open Date] IS NOT NULL
GROUP BY Name
Adding as group by
If you don't need to have it as a separate column, the easiest way would be to add a group by the Tested column as well.
SELECT Name, AVG(DATEDIFF(day,[Open Date],[Close Date])) as "AVG"
FROM Inventory
WHERE [Close Date] IS NOT NULL AND [Open Date] IS NOT NULL
GROUP BY Name, Tested
Then you'll get this structure:
Name
Tested
AVG
aTPO
Yes
56
aTPO
No
50
It's hard to say exactly what you want, but maybe you could use a couple of subqueries?
SELECT Name,
AVG(select DATEDIFF(i2.day,i2.[Open Date],i2.[Close Date]) from inventory i2 where i2.name = i.name and i2.Tested = 'Yes') as "Tested",
AVG(select DATEDIFF(i3.day,i3.[Open Date],i3.[Close Date]) from inventory i3 where i3.name = i.name and i3.Tested = 'No') as "Not Tested"
FROM Inventory i
Where [Close Date] IS NOT NULL AND [Open Date] IS NOT NULL
GROUP BY Name
Out of three columns if you trying to get two columns with its values, you have to specify those columns in your code.
Example:
SELECT column1, column2
FROM table_name
But if you want to find all the columns
Example:
SELECT * FROM table_name

Return only the highest row number for a partitioned column

I'm trying to partition a list of submitted machining jobs by the date they were submitted and return a maximum row number for each partition.
I have tried using Group By, but I want to retain all rows in the result. Partition By does what I need, but I want to display all rows except the maximum row number as blank.
SELECT [Created Date]
,row_number() over(partition by format([Created Date],'d','en-gb') order by [Created Date] desc) AS [Jobs Submitted That Day]
FROM [UK_App].[dbo].[rvxDevMCRequests]
order by [Created Date] desc
Results:
Created Date Jobs Submitted That Day
31/12/2014 1
31/10/2019 1
31/10/2019 2
31/10/2019 3
31/10/2018 1
31/10/2018 2
The order by function is not working correctly, and I can't figure out how to display only the highest row number. I would like it to output this:
Created Date Jobs Submitted That Day
31/12/2014 1
31/10/2018
31/10/2018 2
31/10/2019
31/10/2019
31/10/2019 3
Not an elegant solution:
SELECT [Created Date]
, case when row_number() over(partition by format([Created Date],'d','en-gb') order by [Created Date] desc)
= count(*) over(partition by format([Created Date],'d','en-gb'))
then count(*) over(partition by format([Created Date],'d','en-gb'))
else null end AS [Jobs Submitted That Day]
FROM [UK_App].[dbo].[rvxDevMCRequests]
order by [Created Date] desc
Try this one:
SELECT
a.CreatedDate,
CASE
WHEN y.rnum IS NULL
THEN ''
ELSE
a.JobsSubmitted
END AS JobsSubmitted
FROM
input a
LEFT OUTER JOIN
(
SELECT
x.CreatedDate, x.JobsSubmitted, x.rnum
FROM (
SELECT
a.*,
ROW_NUMBER() OVER(PARTITION BY a.CreatedDate ORDER BY a.JobsSubmitted DESC) AS rnum
FROM
input a
) x
WHERE
x.rnum = 1
) y
ON (
a.CreatedDate = y.CreatedDate
AND a.JobsSubmitted = y.JobsSubmitted
);
SQL Fiddle link for demo: http://www.sqlfiddle.com/#!18/511abf/17
Why are you using format()? There is no reason to convert a date to a string, especially in this case.
One significant issue is that the column [Created Date] has duplicates. When you order by that column, the duplicates can be in any order. In fact, two different order bys on the column in the same query can result in different ordering.
The solution to that is to capture the ordering once in a subquery and then use that:
select [Created Date],
(case when cnt = seqnum then seqnum
end) as [Jobs Submitted That Day]
from (select r.*,
row_number() over (partition by [Created Date] order by [Created Date] desc) as seqnum,
count(*) over (partition by [Created Date]) as cnt
from [UK_App].[dbo].[rvxDevMCRequests]
) r
order by [Created Date] desc, seqnum;
In the above query, seqnum captures the ordering, so it is used for the outer order by.

How to run Row_Number() with filtering

I have a table with multiple rows for each 'Case Number'. I want to pick one row for each Case Number and join this back to another table maintaining a one-to-one relationship.
The conditions to pick this row are :
1) First of all filter out all rows for each Case Number that have Stage = Cancelled
2) If you find Stage = 'In Progress' or 'Paused', pick that row. (Only one of these two can be present for a Case Number)
3) If not (2), then pick Stage = 'Completed' but for the latest 'Stop Time'. (This is where I thought we might have to use ROW_NUMBER())
I've already created a query to push in row numbers and pick up one row based on the latest 'Stop time' but I'm not able to figure out how to add the above filters and if-else conditions in there.
SELECT [Case Number],
ROW_NUMBER ( )
OVER ( PARTITION BY [Case Number] order by [Stop time] desc ) idx
,[Stage]
,[Time left]
,[SLA definition]
,[Elapsed time]
,[Elapsed percentage]
,[Start time]
,[Stop time]
,[Has breached]
,[Breach time]
,[Updated]
,[Updated by]
,[Created]
,[Created by]
FROM ( select * from [SLA_Data] where Stage != 'Cancelled' )v1
It's a bit hard to tell from your question but something like this is my interpretation (I'm not able to access SQL, nor did you provide enough test data so cannot test it)
select * from
(
SELECT [Task],...,...,
ROW_NUMBER ( )
OVER ( PARTITION BY Task order by
case
when Stage in('In Progress' ,'Paused') then 1
when Stage='Completed' then 2 end,
[Stop time] desc ) idx
)
where idx=1
Below code should work assuming Case_Number is present in the below table or the present table must be joined with the Case_Number Table
create view [dbo].[SLA_View] as select * from (
SELECT * FROM
(
SELECT [Task]
,[Stage]
,[Time left]
,[SLA definition]
,[Elapsed time]
,[Elapsed percentage]
,[Start time]
,[Stop time]
,[Has breached]
,[Breach time]
,[Updated]
,[Updated by]
,[Created]
,[Created by]
FROM
(
/* GETS SINGLE CASE FOR MULTIPLE STAGES */
SELECT *,ROW_NUMBER ( ) OVER ( PARTITION BY Task order by [Stage] desc) RNK
FROM [SLA_Data] WHERE [CASE_NUMBER] IN
(
/* GETS DISTINCT CASE NUMBER WITH STAGE = 'PAUSED' OR 'IN PROGRESS' */
SELECT DISTINCT [CASE_NUMBER]
FROM [SLA_Data]
WHERE [Stage] != 'Cancelled'
AND [Stage] IN ('Paused','In Progess')
GROUP BY [CASE_NUMBER]
HAVING COUNT(*) >= 1
)
)Y
WHERE RNK = 1
)Z
UNION
SELECT [Task]
,[Stage]
,[Time left]
,[SLA definition]
,[Elapsed time]
,[Elapsed percentage]
,[Start time]
,[Stop time]
,[Has breached]
,[Breach time]
,[Updated]
,[Updated by]
,[Created]
,[Created by]
FROM
(
SELECT *, ROW_NUMBER ( ) OVER ( PARTITION BY Task order by [start time] desc) idx
FROM [SLA_Data]
WHERE [CASE_NUMBER] NOT IN (
SELECT DISTINCT [CASE_NUMBER]
FROM [SLA_Data]
WHERE [Stage] != 'Cancelled'
AND [Stage] IN ('Paused','In Progess')
GROUP BY [CASE_NUMBER]
HAVING COUNT(*) > 1
)
)v1 where idx = 1
)

How to group some records by name and pivot some values

I am working on a SQL Query to group the results of a View by Id to have only one Row per Id
with a maximum of three pivoted results and keeping some columns static : TestCaseId, TestName, Test Case Num, Owner
Actually this is the Query i Created to get the Desired output but is now working as expected because the MAX is always retriving the max value so i am getting only one row but the pivoted values are repeated to the right.
SELECT DISTINCT TBL1.[TestName], TBL1.[Test Case Num], TBL1.[Owner], MAX(TBL1.[Browser]) as 'Column1', MAX(TBL1.[Run Date]) as 'Column2', MAX(TBL1.[Status]) as 'Column3', MAX(TBL1.[Duration]) as 'Column4', MAX(TBL1.[ErrorMsg]) as 'Column5', MAX(TBL2.[Browser]) as 'Column6', MAX(TBL2.[Run Date]) as 'Column7', MAX(TBL2.[Status]) as 'Column8', MAX(TBL2.[Duration]) as 'Column9', MAX(TBL2.[ErrorMsg]) as 'Column10', MAX(TBL3.[Browser]) as 'Column11' , MAX(TBL3.[Run Date]) as 'Column12', MAX(TBL3.[Status]) as 'Column13', MAX(TBL3.[Duration]) as 'Column14', MAX(TBL3.[ErrorMsg]) as 'Column15'
FROM (SELECT DISTINCT T1.[TestCaseId], T1.[TestName], T1.[Test Case Num], T1.[Owner], T1.[Browser], T1.[Run Date], T1.[Status], T1.[Duration], T1.[ErrorMsg]
FROM [TestRunner].[dbo].RunsRawResults T1) TBL1
cross apply (SELECT DISTINCT T2.[TestCaseId], T2.[Browser], T2.[Run Date], T2.[Status], T2.[Duration], T2.[ErrorMsg]
FROM [TestRunner].[dbo].RunsRawResults T2
WHERE T2.[TestCaseId] = TBL1.[TestCaseId] AND T2.[Run Date] TBL1.[Run Date]) TBL2
cross apply (SELECT DISTINCT T3.[TestCaseId], T3.[Browser], T3.[Run Date], T3.[Status], T3.[Duration], T3.[ErrorMsg]
FROM [TestRunner].[dbo].RunsRawResults T3
WHERE T3.[TestCaseId] = TBL2.[TestCaseId] AND T3.[Run Date] TBL2.[Run Date] AND T3.[Run Date] TBL1.[Run Date]) TBL3
GROUP BY TBL1.[TestCaseId], TBL1.[TestName], TBL1.[Test Case Num], TBL1.[Owner]
Input -
Raw Data (Comes from the RunRawResults View)
Desired and Pivoted Output
Using a common table expression (cte) and row_number() we can simplify the identification and order of multiple run dates. This also lets us skip using distinct and group by.
Switching to outer apply lets us include results where there are less than 3 runs per TestCaseId.
;with cte as (
select *
, rn = row_number() over (
partition by TestCaseId
order by [Run Date]
)
from TestRunner.dbo.RunsRawResults
)
select
tbl1.TestName
, tbl1.[Test Case Num]
, tbl1.Owner
, tbl1.Browser
, [Run Date]_tbl1 = tbl1.[Run Date]
, Status_tbl1 = tbl1.Status
, Duration_tbl1 = tbl1.Duration
, ErrorMsg_tbl1 = tbl1.ErrorMsg
, Browser_tbl2 = tbl2.Browser
, [Run Date]_tbl2 = tbl2.[Run Date]
, Status_tbl2 = tbl2.Status
, Duration_tbl2 = tbl2.Duration
, ErrorMsg_tbl2 = tbl2.ErrorMsg
, Browser_tbl3 = tbl3.Browser
, [Run Date]_tbl3 = tbl3.[Run Date]
, Status_tbl3 = tbl3.Status
, Duration_tbl3 = tbl3.Duration
, ErrorMsg_tbl3 = tbl3.ErrorMsg
from cte as tbl1
outer apply (
select
i.[Run Date]
, i.Status
, i.Duration
, i.ErrorMsg
from cte as i
where i.TestCaseId = tbl1.TestCaseId
and i.rn = 2
) as tbl2
outer apply (
select
i.[Run Date]
, i.Status
, i.Duration
, i.ErrorMsg
from cte as i
where i.TestCaseId = tbl1.TestCaseId
and i.rn = 3
) as tbl3
where tbl1.rn = 1

Subquery returned more than 1 rows inside case statement

I have been working on a query whereby in a subquery I am selecting the column Cust_Status under certain conditions.
select distinct
C.Cust_Code [Cust #],
C.Cust_Start_Date [Start Date],
C.Cust_End_date [End Date],
(select
Cust_Status = (case
when cast(CUST_UPDATE_DATE_LT as DATE) = cast('2017-01-23 00:00:00' as Date)
then 'V'
when cast(CUST_UPDATE_DATE_LT as DATE) = cast('2017-01-22 00:00:00' as Date)
then 'I'
end)
from tblCustomers) [Cust Status],
M.Machine_ID,
M.Machine_Location
from
tblCustomers C
inner join
tblMachine M on C.Cust_Mach_Pkey = M.Pkey
When I run this query I get an error
subquery returned more than 1 value error.
When I remove the subquery inside case, it's fine. But I am sure there is only 1 record present for both date conditions. So not sure how my subquery returning more than 1 values. Please enlighten me.
I am guessing that you just want to compare the latest date. If so, there are much simpler ways:
select C.Cust_Code as [Cust #], C.Cust_Start_Date as [Start Date],
C.Cust_End_date as [End Date],
(case when max(cast(CUST_UPDATE_DATE_LT as DATE)) = '2017-01-23'
then 'V'
when max(cast(CUST_UPDATE_DATE_LT as DATE)) = '2017-01-22'
then 'I'
end) as Cust_status
M.Machine_ID,
M.Machine_Location
from tblCustomers C inner join
tblMachine M
on C.Cust_Mach_Pkey = M.Pkey
group by C.Cust_Code, C.Cust_Start_Date, C.Cust_End_date,
M.Machine_ID, M.Machine_Location