SQL issue: Get Previous data based on a action code - sql-server

I am stuck with this issue.
Please refer to screenshot too for table structure.
SQL ISSUE SCREENSHOT
So we have 2 tables : HISTORY table keeps histiry of payrate, location etc
& POSITION table only gives details of Job action changes datewise.
Desired output we want to only consider Action_code as 'JOB-CHANGE' and get the latest payrate and previous payrate before the action- job change.
--- table1: HISTORY ---
EMPLOYEE
EFFECT_DATE
END_DATE
PAY_RATE
LOCAT_CODE
344
4/1/2021
current - TBD
42.44
ATL
344
3/2/2021
3/31/2021
41.81
ATL
344
3/31/2020
3/1/2021
41.81
DTW
--- table2 ACTION ---
COMPANY
EMPLOYEE
EFFECT_DATE
ACTION_CODE
REASON_01
100
344
3/31/2021
LOA
ST-RTW
100
344
3/2/2021
JOB CHANGE
JC-TRANS(L
100
344
5/31/2020
LOA
ST-COVID90
--- DESIRED OUTPUT --
Employee
Action code
Reason_1
Effective_Date
Previous_Location
Previous_Rate
New_Location
New_Pay_Rate
344
JOB CHANGE
JC-TRANS(L
3/2/2021
DTW
41.81
ATL
42.44
Could you please help me out here. Thanks much :) in advance

You can use OUTER APPLY (SELECT TOP (1) to get the first matching row on or before the date.
Within the APPLY you can use LEAD to access the values on the next row going backwards.
SELECT
a.EMPLOYEE,
[Action code] = h.ACTION_CODE,
a.REASON_01,
Effective_Date = a.EFFECT_DATE,
hPrev.Previous_Location,
hPrev.Previous_Rate,
hNew.New_Location,
hNew.New_Pay_Rate
FROM ACTION a
OUTER APPLY (
SELECT TOP (1)
Previous_Location = h.LOCAT_CODE,
Previous_Rate = h.PAY_RATE
FROM HISTORY h
WHERE h.EFFECT_DATE < a.EFFECT_DATE
AND h.Employee = a.Employee
ORDER BY h.EFFECT_DATE DESC
) hPrev
OUTER APPLY (
SELECT TOP (1)
New_Location = h.LOCAT_CODE,
New_Pay_Rate = h.PAY_RATE,
FROM HISTORY h
WHERE h.EFFECT_DATE > a.EFFECT_DATE
AND h.Employee = a.Employee
ORDER BY h.EFFECT_DATE
) hNew
WHERE a.ACTION_CODE = 'JOB CHANGE';

Related

Sqlserver CTE how to get first non null value

I am trying to use the following query to get a user with his detail and any of his manager or manager's manager id who got a record in UserTag table.
I have got the partial result, but not sure how to get the first non null value. I have show my expected result below.
With Managers AS
(
--initialization
SELECT Id, Email, ManagerID, NULL as UserID
FROM dbo.[User]
WHERE email='user#test.com'
UNION ALL
--recursive execution
SELECT u.Id, u.Email, u.ManagerID, tb2.UserID
FROM dbo.[User] u
INNER JOIN Managers m ON m.ManagerID = u.ID
OUTER apply (select userid from UserTag where userid = m.ManagerID and (TagID=9 or TagName = 'test')) tb2
)
SELECT * FROM Managers
Expected result:
Sample data and expected result:
User table
=========
Id Email ManagerID
--- ----- ---------
11813 userA#test.com 1251
1251 userB#test.com 302
302 userC#test.com 1221
1221 userD#test.com 358
358 userE#test.com 988
988 userE#test.com NULL
100 userA1#test.com 101
101 userB1#test.com 102
102 userC1#test.com 103
103 userD1#test.com 104
104 userF1#test.com NULL
User Tag
Id UserId TagId TagName
1 1221 9 test
2 104 9 test
Expected result
==============
UserId Email TagManagerId
------ ----- ------------
11813 userA#test.com 1221
1251 userB#test.com 1221
302 userC#test.com 1221
Brief description:
I have all users in User table. This is a self referencing table.
I want to get all users whose immediate manager or manager's manager or any manager in his/hierarchy got a record in UserTag table with tagid=9 or tagname='test' as shown here.
Schema db<>fiddle here
Based on your Brief description, I believe that this is an XY Problem
There are solutions to the real need that don't include get first non null value.
What I believe you really need to do is find all the managers that are tagid=9 or tagname='test' and then recursively find all their subordinates. (Run the recursion in the opposite direction to your example query.)
The only complexity I can think of is that such a manager may have a subordinate that also meets those criteria, so I included a condition to avoid recursing them more than once.
WITH
test9_managed_users AS
(
SELECT
u.*,
u.managerid AS closest_test9_manager_id,
0 AS depth
FROM
[user] AS u
INNER JOIN
UserTag AS t
ON t.userid = u.managerid
AND (t.TagID=9 or TagName = 'test')
UNION ALL
SELECT
u.*,
m.closest_test9_manager_id,
m.depth+1
FROM
test9_managed_users AS m
INNER JOIN
[user] AS u
ON u.managerid = m.id
WHERE
-- If a user is themselves a 'test9_manager', don't recurse their children
-- > They would already have been included in the Anchor part of the CTE
NOT EXISTS (
SELECT *
FROM UserTag AS t
WHERE t.userid = m.id
AND (t.TagID=9 or TagName = 'test')
)
)
SELECT
*
FROM
test9_managed_users
ORDER BY
email
Demo with my own test data:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=5e901a6f7db97362bb27ebacb1d4331c
Demo with your test data:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=1ab3625ba797d8fc13c49872d1cd5872

How to place subquery in SQL Server 2008

I have a main SQL query which returns stock quantity in dates between and I also need to return the stock as on date along with between dates result.
Main query:
SELECT
TT.TranRId, MAX(TT.TInQty) AS InQty, MAX(TT.TOutQty) AS OutQty
FROM
TReg TR, TTrans TT
WHERE
TR.TRegId = TT.TrRegId
AND TT.Stid = 2
AND TR.TransDate BETWEEN '2018-08-25' AND '2018-08-28'
GROUP BY
TT.TranRId
ORDER BY
TT.TranRId
Sub query:
(SELECT TT.TransRId, (SUM(TT.TInQty) - SUM(TT.TOutQty))
FROM TTrans TT, TReg TR
WHERE TR.TransDate <= '2018-08-24'
AND TR.TRegId = TT.TrRegId
AND TT.Stid = 2 GROUP BY TT.TranRId) --AS Stock
Please help where I should include my sub query in my main query
To get output as follows:
TransRId Stock InQty OutQty
----------------------------------
41 700 1 1000
42 800 5 500
I am not sure I am following your question 100%, if you are just looking to join it as a sub query the below logic should work.
SELECT TT.TranRId
,MAX(TT.TInQty) AS InQty
,MAX(TT.TOutQty) AS OutQty
,Stock.[Sum]
FROM TReg TR
LEFT JOIN TTrans TT
ON TR.TRegId = TT.TrRegId
LEFT JOIN (
SELECT TT.TransRId
,(SUM(TT.TInQty) - SUM(TT.TOutQty)) as Sum
FROM TTrans TT
LEFT JOIN TReg TR
ON TR.TRegId = TT.TrRegId
WHERE TR.TransDate <= '2018-08-24'
AND TT.Stid = 2
GROUP BY TT.TranRId
) AS Stock
ON Stock.TranRId = TT.TranRId
WHERE TT.Stid = 2
AND TR.TransDate BETWEEN '2018-08-25' AND '2018-08-28'
GROUP BY TT.TranRId
ORDER BY TT.TranRId
edit:
I noticed tt.TranRId and tt.Tran***s***RId if this is not a typo it would need to be corrected, if not my answer will not work for you.
If you need specific dates, including the date in the join logic along with the ID would give you the appropriate result...without knowing your data set I am not sure if the TranRId is unique...sorry about that!

Running 3-Tier T-SQL Query for Report

First question here so please excuse any mistakes...
I am trying to write a SQL Query for an SSRS report and I am totally confused when it comes to my joins.
Background: I have 3 tables that are relevant
Publishers - This is essentially a list of people
Publisher Reports - This is a list of records (related to the Publisher table) that details the work they have completed in a month period.
Report Months - This is a list of records (related to the Publisher Reports table) that relates to a specific month and year. On these records they have an indicator to show whether they relate to the previous six month period.
What i am trying to do is get a list of Publishers who have not submitted a publisher report that is related to a Report Month record within the last 6 months. My desired output is a list of Publishers in on column with the Report Month(s) that they are missing in the next column.
I am really struggling how to do it... I had thought of it as a three step process...
--STEP 1 - Get list of report months that are included in the last 6 months
WITH ACTIVE6MREPM AS
(
SELECT r.jajw_name,
r.jajw_CalendarDate
FROM jajw_reportmonthBase r
WHERE r.jajw_IncludedIn6MonthReport = '1'
),
--STEP 2 - Get list of all publishers
ACTIVEPUBS AS
(
SELECT c.FullName,
c.ContactId
FROM ContactBase c
WHERE c.statecode = '0'
AND c.jajw_CongregationAssignment != 640840001
AND c.jajw_CongregationAssignment != 640840006
AND c.jajw_CongregationAssignment != 640840005
--AND q.jajw_FieldServiceGroups = (#Field_Service_Group)
),
--STEP 3 - Get List of Publisher Reports for the selected Report Months
RELEVANTREPORTS AS
(
SELECT r.jajw_reportId AS Publisher_Report_GUID,
r.jajw_PublisherId AS Publisher_GUID,
r.jajw_ReportMonthId AS ReportMonth_GUID,
m.jajw_name AS ReportMonth_Name
FROM jajw_reportBase r
INNER JOIN jajw_reportmonthBase m ON r.jajw_ReportMonthId = m.jajw_reportmonthId
WHERE r.jajw_ReportPeriod6Month = '1'
ORDER BY m.jajw_CalendarDate
After these three, I want to create my list as described above and this is the bit that has me stumped! Any help would be greatly appreciated!
Thanks!
I think you can shorten your code a lot... here's a shot at it without having test data... be sure to read the comments and add in the join condition and check the where clause.
with cte as(
SELECT r.jajw_reportId AS Publisher_Report_GUID,
r.jajw_PublisherId AS Publisher_GUID,
r.jajw_ReportMonthId AS ReportMonth_GUID,
m.jajw_name AS ReportMonth_Name,
c.FullName,
c.ContactId
FROM jajw_reportBase r
INNER JOIN jajw_reportmonthBase m ON
r.jajw_ReportMonthId = m.jajw_reportmonthId
INNER JOIN ContactBase c on --what ever condition is appropiate
WHERE r.jajw_ReportPeriod6Month = '1'
and m.jajw_IncludedIn6MonthReport = '1' --maybe put this here, or does the above do the same thing?
ORDER BY m.jajw_CalendarDate)
select
p2.FullName,
p2.ReportMonth_Name
from cte p
right join(select distinct
ReportMonth_Name, FullName
from ContactBase
left join jajw_reportmonthBase on 1=1) p2 on p2.FullName = p.FullName and p2.ReportMonth_Name = p.ReportMonth_Name
where
ContactId in (select ContactId from cte group by ContactId having count(distinct ReportMonth_GUID) < 6)
and p.FullName is null
Here is an example using test data.
declare #pub table (pubname varchar(56), ReportMonthName varchar(16))
insert into #pub (pubname,ReportMonthName) values
('a','Jan'),
('a','Feb'),
('a','Mar'),
('a','Apr'),
--publisher a is missing May and Jun
('b','Jan'),
('b','Feb'),
('b','Mar'),
('b','Jun')
--publisher b is missing Apr and May
declare #dt table (ReportMonthName varchar(16))
insert into #dt (ReportMonthName) values
('Jan'),
('Feb'),
('Mar'),
('Apr'),
('May'),
('Jun')
select
p2.pubname
,p2.ReportMonthName
from #pub p
right join(
select distinct
p.pubname
,d.ReportMonthName
from #pub p
left join #dt d on 1=1)p2 on p2.pubname = p.pubname and p2.ReportMonthName = p.ReportMonthName where p.pubname is null

How to display only the MAX results from a query

I am new to writing MS SQL queries and I am trying to display only the record with the highest field named RecordVersion.
Below is the query that works but displays all records:
SELECT
PriceCalendars.PriceProgramID,
PriceCalendars.EffectiveDateTime,
PriceSchedules.Price,
PriceSchedules.PLU,
items.Descr,
PriceSchedules.LastUpdate,
PriceSchedules.LastUpdatedBy,
PriceSchedules.RecordVersion,
PriceSchedules.PriceScheduleUniqueID
FROM
PriceCalendars
INNER JOIN PriceSchedules ON PriceCalendars.PriceProgramID = PriceSchedules.PriceProgramID
INNER JOIN items ON PriceSchedules.PLU = items.PLU
WHERE
(PriceSchedules.PLU = 'SLS10100103')
AND (PriceCalendars.EffectiveDateTime = '2016-03-22')
Here are the query results:
PriceProgramID EffectiveDateTime Price PLU Descr LastUpdate LastUpdatedBy RecordVersion PriceScheduleUniqueID
1 2016-03-22 00:00:00.000 35.00 SLS10100103 Architecture Adult from NP POS 2015-01-22 07:53:15.000 GX70,83 9 569
1 2016-03-22 00:00:00.000 32.00 SLS10100103 Architecture Adult from NP POS 2014-02-25 16:22:46.000 GX70,83 5 86180
The first line of the results has RecordVersion being 9 and the second line results is 5, I only want the higher record displaying, the one that returned RecordVersion = 9.
Every time I try to use the MAX command I get errors or the group by and I have tried every example I could find on the web but nothing seems to work.
Using MS SQL 2012.
Thanks,
Ken
Try the following query which attempts to solve your problem by ordering the returned rows by RecordVersion DESC and then SELECTs just the first row.
SELECT TOP 1
PriceCalendars.PriceProgramID,
PriceCalendars.EffectiveDateTime,
PriceSchedules.Price,
PriceSchedules.PLU,
items.Descr,
PriceSchedules.LastUpdate,
PriceSchedules.LastUpdatedBy,
PriceSchedules.RecordVersion,
PriceSchedules.PriceScheduleUniqueID
FROM
PriceCalendars
INNER JOIN PriceSchedules ON PriceCalendars.PriceProgramID = PriceSchedules.PriceProgramID
INNER JOIN items ON PriceSchedules.PLU = items.PLU
WHERE
(PriceSchedules.PLU = 'SLS10100103')
AND (PriceCalendars.EffectiveDateTime = '2016-03-22')
ORDER BY
RecordVersion DESC
All group by columns should be in select ,that's the rule of group by.How group by works is for every distinct combination of group by columns,arrange remaining columns into groups,so that any aggregation can be applied,in your case I am not sure what group by columns are unique with out test date.here is one version which use row number which gives you the output desired
Remember ,order by last updated date is the one which decides rows order and assign numbers
WITH CTE
AS
(
SELECT PriceCalendars.PriceProgramID,
PriceCalendars.EffectiveDateTime,
PriceSchedules.Price,
PriceSchedules.PLU,
items.Descr,
PriceSchedules.LastUpdate,
PriceSchedules.LastUpdatedBy,
PriceSchedules.RecordVersion,
PriceSchedules.PriceScheduleUniqueID,
ROW_NUMBER() OVER (PARTITION BY PriceSchedules.RecordVersion ORDER BY PriceSchedules.LastUpdatedBy) AS RN
FROM
PriceCalendars
INNER JOIN PriceSchedules ON PriceCalendars.PriceProgramID = PriceSchedules.PriceProgramID
INNER JOIN items ON PriceSchedules.PLU = items.PLU
WHERE
(PriceSchedules.PLU = 'SLS10100103')
AND (PriceCalendars.EffectiveDateTime = '2016-03-22')
)
SELECT * FROM CTE WHERE RN=1

Find Previous record by date stored in another Table

So i have 2 tables, one keeps the record of the cash on tjhe cash register at the end of the day and how much money is left for the next day, the other keeps a record of basicly the date of the record (tables cannot be joined) All that looks like this:
RegistersTable
------------------
Register_ID| DATE
5 | 02/02/2012
8 | 04/02/2012
1 | 10/02/2012
CashTable
----------------
Register_ID|CashEOD|CashFND
8 |3235 |325
5 |6843 |435
1 |1236 |1953
So what im trying to get is a select statement that should return this
RegisterID| DATE|CashEOD|PrevCashFND
1 |10/02/2012|1236 |325
8 |04/02/2012|3235 |435
5 |02/02/2012|6843 |0/Null
Start with a RegisterID on the CashTable, find the previous RegisterID by the DATE in RegistersTable, get the previous CashFND so the final goal is to know how much was selled on that day.
Cash End of Day minus the Cash left on the register from the previous day should tell me that. Thanks in advance.
Please try the following - I hope it helps:
SELECT
D.RegisterID,
D.[DATE],
CashTable.CashEOD,
CashTable_Prev.CashFND AS PrevCashFND
FROM
(
SELECT
A.RegisterID,
A.[DATE],
B.RegisterID AS PrevID
FROM
RegistersTable A
INNER JOIN RegistersTable B ON A.RegisterID <> B.RegisterID AND A.[DATE] > B.[DATE]
INNER JOIN
(
SELECT
_innA.RegisterID,
_innB.RegisterID As PrevID,
MIN(_innA.[DATE] - _innB.[DATE]) AS MinDateDiff
FROM
RegistersTable _innA
INNER JOIN RegistersTable _innB ON _innA.RegisterID <> _innB.RegisterID AND _innA.[DATE] > _innB.[DATE]
GROUP BY
_innA.RegisterID,
_innB.RegisterID
) C
ON A.RegisterID = C.RegisterID AND B.RegisterID = C.PrevID AND (A.[DATE] - B.[DATE]) = C.MinDateDiff
) AS D
INNER JOIN CashTable
ON CashTable.Register_ID = D.RegisterID
INNER JOIN CashTable AS CashTable_Prev
ON CashTable_Prev.Register_ID = D.PrevID

Resources