Check if Active Inactive date lies within the Date Range of another Active Inactive Date - sql

Table 1
Loc_Id
Label_Id
Active_Date
Inactive_Date
1
1001
2022/05/13
9999/12/31
2
1001
2018/05/20
2022/05/12
3
1001
2012/06/14
2018/05/12
Table 2
Label_Id
Tab2_Active_Date
Tab2_Inactive_Date
1001
2022/05/13
9999/12/31
1001
2018/05/22
2022/05/12
1001
2012/06/14
2018/05/12
I want to know which records in Table2 have Tab2_Active Date > Active Date in Table 1 and Tab2_Inactive Date < Inactive Date in Table 1.
For example in this the scenario the date Tab2_Active Date 2018/05/22 mentioned in Table 2 is greater than 2018/05/20 mentioned in table 1.
So the o/p will be
Loc_Id
Tab2_Active_Date
Tab2_Inactive_Date
2
2018/05/22
2022/05/12
Since I only have only Ids to join as the keys for 2 tables and I need to compare the dates, I cannot take dates to join the tables which results in inaccurate data.

Create table #T1
(
Loc_Id int,
Label_Id int,
Active_Date date,
Inactive_Date date
)
Create table #T2
(
Label_Id int,
Active_Date date,
Inactive_Date date
)
Insert into #T1
Select 1, 1001, '2022-05-13', '9999-12-31'
union
Select 2, 1001, '2022-05-20', '2022-05-12'
union
Select 3, 1001, '2022-06-14', '2018-05-12'
union
Select 4, 1001, '2022-07-14', '2018-08-13'
Insert into #T2
Select 1001, '2022-05-13', '9999-12-31'
union
Select 1001, '2022-05-22', '2022-05-12'
union
Select 1001, '2022-06-14', '2018-05-12'
union
Select 1001, '2022-06-14', '2018-05-12'
union
Select 1001, '2022-07-14', '2018-08-12'
;with Cte as
(
Select Label_Id, Active_Date, Inactive_Date from #T2
EXCEPT
Select Label_Id, Active_Date, Inactive_Date from #T1
)
Select t1.Loc_Id, t2.Active_Date, t2.Inactive_Date
from #T1 t1
inner join Cte t2 on t1.Label_Id = t2.Label_Id and (t2.Active_Date > t1.Active_Date and t2.Inactive_Date = t1.Inactive_Date)
union
Select t1.Loc_Id, t2.Active_Date, t2.Inactive_Date
from #T1 t1
inner join Cte t2 on t1.Label_Id = t2.Label_Id and (t2.Inactive_Date < t1.Inactive_Date and t2.Active_Date = t1.Active_Date)
Drop table #T1
Drop table #T2

Here's what I came up with
with t1 as (
select * from (values
(1, 1001, '2022-05-13', '9999-12-31'),
(2, 1001, '2018-05-20', '2022-05-12'),
(3, 1001, '2012-06-14', '2018-05-12')
) as x(Loc_ID, Label_Id, Active_Date, Inactive_Date)
),
t2 as (
select * from (values
(1001, '2022-05-13', '9999-12-31'),
(1001, '2018-05-22', '2022-05-12'),
(1001, '2012-06-14', '2018-05-12')
) as x(Label_Id, Active_Date, Inactive_Date)
)
select t1.*, '||', t2.*
from t1
join t2
on t2.Active_Date >= t1.Active_Date
and t2.Inactive_Date <= t1.Inactive_Date
and (
t1.Active_Date <> t2.Active_Date
or t1.Inactive_Date <> t2.Inactive_Date
)
Ignoring the CTEs (that's just a way to get the data into a tabular structure), the join criteria in the SELECT statement say that there must be partial overlap in the interval (which are the first two predicates on only one of active_date or inactive_date) but not complete overlap (which is the compound predicate saying that at least one of active_date or inactive_date must not match).

Related

SQL How to find the Data which is not in table

I have this select statement, where I want to show found and not-found in the result set ...
My query gives me only whatever (values) exists in the DB.
How to also add not-found.
Example:
5647994 1234 Data exist in table
5651061 8976 Data exist in table
5823683 null Data not exist in table
6115602 null Data not exist in table
SELECT *
FROM Carrier c
WHERE (SUBSTRING(c.SrcFileName, 14, 7) in (
'5647994',
'5651061',
'5823683',
'6115602',
'6125795',
'6140114',
'6144781',
'6155133')
Try this:
SELECT t1.val,
IF (t2.id IS NULL, 'NOT FOUND', 'FOUND'),
t2.*
FROM (
SELECT '5647994' AS val UNION ALL SELECT '5651061' UNION ALL
SELECT '5823683' UNION ALL SELECT '6115602' UNION ALL
SELECT '6125795' UNION ALL SELECT '6140114' UNION ALL
SELECT '6144781' UNION ALL SELECT '6155133') AS t1
LEFT JOIN Carrier AS t2 ON t1.val = SUBSTRING(t2.SrcFileName, 14, 7)
The idea is to create an in-line table that contains all to-be-searched values. If we LEFT JOIN the original table to this in-line table, then all values are returned.
The above query assumes that id is a field of Carrier table. Checking this field for NULL/NOT NULL values identifies not found / found values respectively.
Updated based upon additional information.
If(OBJECT_ID('tempdb..#TempSrcFileName') Is Not Null) Drop Table #TempSrcFileName
CREATE TABLE #TempSrcFileName
(
src_file_name nchar(7)
)
INSERT INTO #TempSrcFileName (src_file_name)
VALUES
('5647994')
, ('5651061')
, ('5823683')
, ('6115602')
, ('6125795')
, ('6140114')
, ('6144781')
, ('6155133')
;
SELECT
t.src_file_name
, 'Found' AS [Status]
FROM #TempSrcFileName t
LEFT JOIN Carrier c ON SUBSTRING(c.SrcFileName, 14, 7) = t.src_file_name
WHERE (SUBSTRING(c.SrcFileName, 14, 7) IS NOT NULL)
UNION SELECT
t.src_file_name
, 'Not Found' AS [Status]
FROM #TempSrcFileName t
LEFT JOIN Carrier c ON SUBSTRING(c.SrcFileName, 14, 7) = t.src_file_name
WHERE (SUBSTRING(c.SrcFileName, 14, 7) IS NULL)

Inserting missing rows with a join

I have a SQL script that returns this derived table.
MM/YYYY Cat Score
01/2012 Test1 17
02/2012 Test1 19
04/2012 Test1 15
05/2012 Test1 16
07/2012 Test1 14
08/2012 Test1 15
09/2012 Test1 15
12/2012 Test1 11
01/2013 Test2 10
02/2013 Test2 15
03/2013 Test2 13
05/2013 Test2 18
06/2013 Test2 14
08/2013 Test2 15
09/2013 Test2 14
12/2013 Test2 10
As you can see, I am missing some MM/YYYYs (03/2012, 06/2012, 11/2012, etc).
I would like to fill in the missing MM/YYYYs with the Cat & a 0 (zero) form the score.
I have tried to join a table that contains the all MM/YYYY for the ranges the query will be run, but this only returns the missing rows for the first occurrence, it does not repeat for each Cat (should have known that).
So my question is this, can I do this using a join or will I have to do this in a temp table, and then output the data.
AHIGA,
LarryR…
You need to cross join your categories and a list of all dates in the range. Since you have posted no table structures I'll have to guess at your structure slightly, but assuming you have a calendar table you can use something like this:
SELECT calendar.Date,
Category.Cat,
Score = ISNULL(Scores.Score, 0)
FROM Calendar
CROSS JOIN Catogory
LEFT JOIN Scores
ON Scores.Cat = Category.Cat
AND Scores.Date = Calendar.Date
WHERE Calendar.DayOfMonth = 1;
If you do not have a calendar table you can generate a list of dates using the system table Master..spt_values:
SELECT Date = DATEADD(MONTH, Number, '20120101')
FROM Master..spt_values
WHERE Type = 'P';
Where the hardcoded date '20120101' is the first date in your range.
ADDENDUM
If you need to actually insert the missing rows, rather than just have a query that fills in the blanks you can use this:
INSERT Scores (Date, Cat, Score)
SELECT calendar.Date,
Category.Cat,
Score = 0
FROM Calendar
CROSS JOIN Catogory
WHERE Calendar.DayOfMonth = 1
AND NOT EXISTS
( SELECT 1
FROM Scores
WHERE Scores.Cat = Category.Cat
AND Scores.Date = Calendar.Date
)
Although, in my opinion if you have a query that fills in the blanks inserting the data is a bit of a waste of time.
To get what you want, start with a driver table and then use left outer join. The result is something like this:
select driver.cat, driver.MMYYYY, coalesce(t.score, 0) as score
from (select cat, MMYYYY
from (select distinct cat from t) c cross join
themonths -- use where to get a date range
) driver left outer join
t
on t.cat = driver.cat and t.MMMYYYY = driver.MMYYYY
Try this one -
DECLARE #temp TABLE (FDOM DATETIME, Cat NVARCHAR(50), Score INT)
INSERT INTO #temp (FDOM, Cat, Score)
VALUES
('20120101', 'Test1', 17),('20120201', 'Test1', 19),
('20120401', 'Test1', 15),('20120501', 'Test1', 16),
('20120701', 'Test1', 14),('20120801', 'Test1', 15),
('20120901', 'Test1', 15),('20121001', 'Test1', 13),
('20121201', 'Test1', 11),('20130101', 'Test1', 10),
('20130201', 'Test1', 15),('20130301', 'Test1', 13),
('20130501', 'Test1', 18),('20130601', 'Test1', 14),
('20130801', 'Test1', 15),('20130901', 'Test1', 14),
('20131201', 'Test1', 10),('20120601', 'Test2', 10)
;WITH enum AS
(
SELECT Cat, StartDate = MIN(FDOM), EndDate = MAX(FDOM)
FROM #temp
GROUP BY Cat
UNION ALL
SELECT Cat, DATEADD(MONTH, 1, StartDate), EndDate
FROM enum
WHERE StartDate < EndDate
)
SELECT e.StartDate, t.Cat, Score = ISNULL(t.Score, 0)
FROM enum e
LEFT JOIN #temp t ON e.StartDate = t.FDOM AND e.Cat = t.Cat
ORDER BY e.StartDate, t.Cat
Do a left join from "complete table" to "incomplete table" and set a where statement to check the date column of the "incomplete" table. So you will only get the missing results in your select query. After that, just set a "insert into tablename" before.
In the first run it will find two rows, that aren't already in the incomplete table. So it will be inserted by the insert into statement, two rows affected. In a second run the result in the select statement has 0 rows, so nothing happens. Zero rows affected :-)
Sample: http://sqlfiddle.com/#!2/895fe/6
(Just mark the select statement; the insert into statement isn't required to just see, how the join works)
Insert Into supportContacts
Select * FROM
(
Select
'01/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'02/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'03/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'04/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'05/2012' as DDate, 'Test1' as Cat, 17 as Score
) CompleteTable
LEFT JOIN
(
Select
'01/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'02/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'03/2012' as DDate, 'Test1' as Cat, 17 as Score
) InCompleteTable
ON CompleteTable.DDate = IncompleteTable.DDate
WHERE IncompleteTable.DDate is null

difficult sql query

I have a table containing many columns, I have to make my selection according to these two columns:
TIME ID
-216 AZA
215 AZA
56 EA
-55 EA
66 EA
-03 AR
03 OUI
-999 OP
999 OP
04 AR
87 AR
The expected output is
TIME ID
66 EA
03 OUI
87 AR
I need to select the rows with no matches. There are rows which have the same ID, and almost the same time but inversed with a little difference. For example the first row with the TIME -216 matches the second record with time 215. I tried to solve it in many ways, but everytime I find myself lost.
First step -- find rows with duplicate IDs. Second step -- filter for rows which are near-inverse duplicates.
First step:
SELECT t1.TIME, t2.TIME, t1.ID FROM mytable t1 JOIN mytable
t2 ON t1.ID = t2.ID AND t1.TIME > t2.TIME;
The second part of the join clause ensures we only get one record for each pair.
Second step:
SELECT t1.TIME,t2.TIME,t1.ID FROM mytable t1 JOIN mytable t2 ON t1.ID = t2.ID AND
t1.TIME > t2.TIME WHERE ABS(t1.TIME + t2.TIME) < 3;
This will produce some duplicate results if eg. (10, FI), (-10, FI) and (11, FI) are in your table as there are two valid pairs. You can possibly filter these out as follows:
SELECT t1.TIME,MAX(t2.TIME),t1.ID FROM mytable t1 JOIN mytable t2 ON
t1.ID = t2.ID AND t1.TIME > t2.TIME WHERE ABS(t1.TIME + t2.TIME) < 3 GROUP BY
t1.TIME,t1.ID;
But it's unclear which result you want to drop. Hopefully this points you in the right direction, though!
Does this help?
create table #RawData
(
[Time] int,
ID varchar(3)
)
insert into #rawdata ([time],ID)
select -216, 'AZA'
union
select 215, 'AZA'
union
select 56, 'EA'
union
select -55, 'EA'
union
select 66, 'EA'
union
select -03, 'AR'
union
select 03, 'OUI'
union
select -999, 'OP'
union
select 999, 'OP'
union
select 04, 'AR'
union
select 87, 'AR'
union
-- this value added to illustrate that the algorithm does not ignore this value
select 156, 'EA'
--create a copy with an ID to help out
create table #Data
(
uniqueId uniqueidentifier,
[Time] int,
ID varchar(3)
)
insert into #Data(uniqueId,[Time],ID) select newid(),[Time],ID from #RawData
declare #allowedDifference int
select #allowedDifference = 1
--find duplicates with matching inverse time
select *, d1.Time + d2.Time as pairDifference from #Data d1 inner join #Data d2 on d1.ID = d2.ID and (d1.[Time] + d2.[Time] <=#allowedDifference and d1.[Time] + d2.[Time] >= (-1 * #allowedDifference))
-- now find all ID's ignoring these pairs
select [Time],ID from #data
where uniqueID not in (select d1.uniqueID from #Data d1 inner join #Data d2 on d1.ID = d2.ID and (d1.[Time] + d2.[Time] <=3 and d1.[Time] + d2.[Time] >= -3))

SQL Query for retrieving records separated by seconds

I am trying to write a Microsoft SQL Server query for retrieving the oldest record in which the text fields are the same, but the dates are 30 seconds or less apart. Here is an example:
My table:
RecordID TextField1 TextField2 DateField1
--------------------------------------------------------------------------------
1 SomeData1 SomeData2 9/11/2011 2:33:00pm
2 SomeData3 SomeData4 9/11/2011 2:33:15pm
3 SomeData3 SomeData4 9/11/2011 2:33:18pm
4 SomeData3 SomeData4 9/11/2011 2:42:12pm
5 SomeData1 SomeData2 9/11/2011 2:33:01pm
6 SomeData6 SomeData7 9/11/2011 2:33:01pm
7 SomeData1 SomeData2 9/12/2011 2:33:00pm
8 SomeData6 SomeData8 9/11/2011 2:33:03pm
Okay, so in this example, I want a query that will pull the rows in which TextField1=TextField1 and TextField2=TextField2 and the dates between them are 30 seconds or less (I want the oldest of the two returned). So the query, in this example, should return:
RecordID TextField1 TextField2 DateField1
--------------------------------------------------------------------------------
1 SomeData1 SomeData2 9/11/2011 2:33:00pm
2 SomeData3 SomeData4 9/11/2011 2:33:15pm
RecordID 8 is not returned because TextField2 is different.
Hopefully I explained this clearly enough. Any help would be appreciated!
I couldn't understand everything on your question.
This is a generic SQL query that will compare the records of you table, against the same table, looking for records with different RecordID, but equal TextField1 and TextField2.
Leave a comment if this looks like what you want and we can improve this query to get exactly what you are looking for.
UPDATED:
SELECT * FROM my_table AS t1
INNER JOIN my_table AS t2
ON (
t1.RecordID < t2.recordID
AND
DATEDIFF(second, t1.DateField1, t2.DateField1) <= 30
AND
t1.TextField1 = t2.TextField1
AND
t2.TextField2 = t1.TextField2
);
This returns the two records you are looking for in your example. The join between t1 and t2 returns the records that meet your criteria, and then joining to t3 returns the oldest of the rows meeting the criteria.
;
with TestCTE(RecordID, TextField1, TextField2, DateField1)
as
(
select 1, 'SomeData1', 'SomeData2', cast('9/11/2011 2:33:00pm' as datetime)
union
select 2, 'SomeData3', 'SomeData4', cast('9/11/2011 2:33:15pm' as datetime)
union
select 3, 'SomeData3', 'SomeData4', cast('9/11/2011 2:33:18pm' as datetime)
union
select 4, 'SomeData3', 'SomeData4', cast('9/11/2011 2:42:12pm' as datetime)
union
select 5, 'SomeData1', 'SomeData2', cast('9/11/2011 2:33:01pm' as datetime)
union
select 6, 'SomeData6', 'SomeData7', cast('9/11/2011 2:33:01pm' as datetime)
union
select 7, 'SomeData1', 'SomeData2', cast('9/12/2011 2:33:00pm' as datetime)
union
select 8, 'SomeData6', 'SomeData8', cast('9/11/2011 2:33:03pm' as datetime)
)
select t1.*
from TestCTE t1
join TestCTE t2 on t1.RecordID <> t2.RecordID
and t1.TextField1 = t2.TextField1
and t1.TextField2 = t2.TextField2
and datediff(second, t1.DateField1, t2.DateField1) <= 30
join
(
select TextField1, TextField2, min(DateField1) as MinDate
from TestCTE
group by TextField1, TextField2
) t3 on t1.TextField1 = t3.TextField1
and t1.TextField2 = t3.TextField2
and t1.DateField1 = t3.MinDate
Sounds like a simple self-join and not trying to make it more than it should. By doing the self-join and applying the group by, the following should get it done for you.
The self-join is on both text fields, and the first table's Record ID always GREATER than the one in the second table.... Then, the comparison on the date/time factor of 30 seconds.
Due to a comment from ADrift, and re-looking at the data, what I THOUGHT was a date/time stamp field on the record would always be increasing is not always the case... Slight change... Get the latest date/time for the given text1 and text2, then re-join back for rest of the details.
select
YT3.*
from
( select
YT.TextField1,
YT.TextField2,
MIN( YT.DateField1) OldestDateTime,
from
YourTable YT
Join YourTable YT2
on YT.TextField1 = YT2.TextField1
AND YT.TextField2 = YT2.TextField2
AND YT.RecordID > YT2.RecordID
AND datediff(second, YT.DateField1, YT2.DateField1) <= 30
group by
YT.TextField1,
YT.TextFIeld2 ) PreQuery
JOIN YourTable YT3
on PreQuery.TextField1 = YT3.TextField1
AND PreQuery.TextField2 = YT3.TextField2
AND PreQuery.OldestDateTime = YT3.DateField1
order by
whatever...
Assuming there can only be no more than two adjacent rows (those within 30 second from each other) and you are on SQL Server 2005 or later version:
WITH sampledata (RecordID, TextField1, TextField2, DateField1) AS
(
SELECT 1, 'SomeData1', 'SomeData2', CAST('20110911 14:33:00' AS datetime) UNION ALL
SELECT 2, 'SomeData3', 'SomeData4', CAST('20110911 14:33:15' AS datetime) UNION ALL
SELECT 3, 'SomeData3', 'SomeData4', CAST('20110911 14:33:18' AS datetime) UNION ALL
SELECT 4, 'SomeData3', 'SomeData4', CAST('20110911 14:42:12' AS datetime) UNION ALL
SELECT 5, 'SomeData1', 'SomeData2', CAST('20110911 14:33:01' AS datetime) UNION ALL
SELECT 6, 'SomeData6', 'SomeData7', CAST('20110911 14:33:01' AS datetime) UNION ALL
SELECT 7, 'SomeData1', 'SomeData2', CAST('20110912 14:33:00' AS datetime) UNION ALL
SELECT 8, 'SomeData6', 'SomeData8', CAST('20110911 14:33:03' AS datetime)
),
ranked AS (
SELECT
*,
rn = ROW_NUMBER() OVER (PARTITION BY TextField1, TextField2 ORDER BY DateField1)
FROM sampledata
),
SELECT
r1.RecordID,
r1.TextField1,
r1.TextField2,
r1.DateField1
FROM ranked r1
INNER JOIN ranked r2 ON r1.TextField1 = r2.TextField1
AND r1.TextField2 = r2.TextField2
AND r1.rn = r2.rn - 1
WHERE r2.DateField1 BETWEEN r1.DateField1 AND DATEADD(SECOND, 30, r1.DateField1)
Output:
RecordID TextField1 TextField2 DateField1
----------- ---------- ---------- -----------------------
1 SomeData1 SomeData2 2011-09-11 14:33:00.000
2 SomeData3 SomeData4 2011-09-11 14:33:15.000

SQL Running Subtraction

I have a result set as below:
Item ExpectedQty ReceivedQty Short
Item01 30 45 5
Item01 20 45 5
Item02 40 38 2
item03 50 90 10
item03 30 90 10
item03 20 90 10
query is:
select a.Item, a.ExpectedQty,b.ReceivedQty, b.Short
from a join b on a.Item = b.Item
I need to get result as in second chart. Basically I have a total of received quantity in each line and I need to show received quantity against Expected Quantity, if there is any shortage I need to show in last line.
Expected:
Item ExpectedQty ReceivedQty Short
item01 30 30 0
item01 20 15 5
item02 40 38 2
item03 50 50 0
item03 30 30 0
item03 20 10 10
Thanks in advance.
Edited,
Vession 02 ;
-- Just a brief of business scenario is table has been created for a good receipt.
-- So here we have good expected line with PurchaseOrder(PO) in first few line.
-- And then we receive each expected line physically and that time these
-- quantity may be different
-- due to business case like quantity may damage and short quantity like that.
-- So we maintain a status for that eg: OK, Damage, also we have to calculate
-- short quantity
-- based on total of expected quantity of each item and total of received line.
if object_id('DEV..Temp','U') is not null
drop table Temp
CREATE TABLE Temp
(
ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
Item VARCHAR(32),
PO VARCHAR(32) NULL,
ExpectedQty INT NULL,
ReceivedQty INT NULL,
[STATUS] VARCHAR(32) NULL,
BoxName VARCHAR(32) NULL
)
-- Please see first few line with PO data will be the expected lines,
-- and then rest line will be received line
INSERT INTO TEMP (Item,PO,ExpectedQty,ReceivedQty,[STATUS],BoxName)
SELECT 'ITEM01','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01','PO-02','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM02','PO-01','40',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-01','50',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-02','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-03','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM04','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01',NULL,NULL,'20','OK','box01' UNION ALL
SELECT 'ITEM01',NULL,NULL,'25','OK','box02' UNION ALL
SELECT 'ITEM01',NULL,NULL,'5','DAMAGE','box03' UNION ALL
SELECT 'ITEM02',NULL,NULL,'38','OK','box04' UNION ALL
SELECT 'ITEM02',NULL,NULL,'2','DAMAGE','box05' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box06' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box07' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box08' UNION ALL
SELECT 'ITEM03',NULL,NULL,'10','DAMAGE','box09' UNION ALL
SELECT 'ITEM04',NULL,NULL,'25','OK','box10'
-- Below Table is my expected result based on above data.
-- I need to show those data following way.
-- So I appreciate if you can give me an appropriate query for it.
-- Note: first row is blank and it is actually my table header. :)
SELECT ''as'ITEM', ''as'PO#', ''as'ExpectedQty',''as'ReceivedQty',
''as'DamageQty' ,''as'ShortQty' UNION ALL
SELECT 'ITEM01','PO-01','30','30','0' ,'0' UNION ALL
SELECT 'ITEM01','PO-02','20','15','5' ,'0' UNION ALL
SELECT 'ITEM02','PO-01','40','38','2' ,'0' UNION ALL
SELECT 'ITEM03','PO-01','50','50','0' ,'0' UNION ALL
SELECT 'ITEM03','PO-02','30','30','0' ,'0' UNION ALL
SELECT 'ITEM03','PO-03','20','10','10','0' UNION ALL
SELECT 'ITEM04','PO-01','30','25','0' ,'5'
One part of the problem is to get the running totals of expected item qunatities. For that you'd need a way to distinguish rows with same items from each other and a rule for the order of discharging same item quantities.
For the purpose of my attempt at solving your problem I'm going to assume there's a timestamp column whose values provide the order of discharge and are unique within same item groups.
Here's the sample data definition I was testing my solution on:
CREATE TABLE TableA (Item varchar(50), ExpectedQty int, Timestamp int);
INSERT INTO TableA
SELECT 'Item01', 30, 1 UNION ALL
SELECT 'Item01', 20, 2 UNION ALL
SELECT 'Item02', 40, 1 UNION ALL
SELECT 'item03', 50, 1 UNION ALL
SELECT 'item03', 30, 2 UNION ALL
SELECT 'item03', 20, 3;
CREATE TABLE TableB (Item varchar(50), ReceivedQty int);
INSERT INTO TableB
SELECT 'Item01', 45 UNION ALL
SELECT 'Item02', 38 UNION ALL
SELECT 'item03', 90;
And here's my solution:
SELECT
Item,
ExpectedQty,
ReceivedQty = CASE
WHEN RemainderQty >= 0 THEN ExpectedQty
WHEN RemainderQty < -ExpectedQty THEN 0
ELSE RemainderQty + ExpectedQty
END,
Short = CASE
WHEN RemainderQty >= 0 THEN 0
WHEN RemainderQty < -ExpectedQty THEN ExpectedQty
ELSE ABS(RemainderQty)
END
FROM (
SELECT
a.Item,
a.ExpectedQty,
RemainderQty = b.ReceivedQty - a.RunningTotalQty
FROM (
SELECT
a.Item,
a.Timestamp,
a.ExpectedQty,
RunningTotalQty = SUM(a2.ExpectedQty)
FROM TableA a
INNER JOIN TableA a AS a2 ON a.Item = a2.Item AND a.Timestamp >= a2.Timestamp
GROUP BY
a.Item,
a.Timestamp,
a.ExpectedQty
) a
INNER JOIN TableB b ON a.Item = b.Item
) s
select a.Item, a.ExpectedQty,b.ReceivedQty, (a.ExpectedQty - b.ReceivedQty) as 'Short' from a join b on a.Item = b.Item
SELECT a.ExpectedQty,
b.ReceivedQty,
CASE WHEN b.ReceivedQty < a.ExpectedQty
THEN b.ReceivedQty - a.ExpectedQty
ELSE 0
END Short
FROM dbo.a a
INNER JOIN dbo.b b
ON a.ItemId = b.ItemId