How to count lines backwards from log in TSQL? - sql-server

I would like to count the amount of pallets on a location where you can't see on the location how many pallets there are. The only way to find out is to assume that you can count backwards from the log where it's logged what pallets have been moved to that location.
Location quantity
LOCATION QUANTITY
Loc_1 20
Log
MOVED_TO_LOCATION QUANTITY PALLET_NUMBER
Loc_1 5 13
Loc_1 5 12
Loc_1 5 11
Loc_1 5 10 <-- Count the lines from here and up (count = 4).
Loc_1 5 9
...
What I tried so far was some CROSS APPLY where I joined the location to the log and counted the results. Which returned 13 (instead of 4) because it didn't count away the ones that were already gone. So how would I count backwards and only gets the first 4 rows?
-- Query returns 13 rows.
CROSS APPLY (
SELECT PALL_NUM
FROM LOG
WHERE MOVED_TO_LOCATION = LOCATION
ORDER BY TIMESTAMP DESC) LOG
I assume you could somehow incrementally count up the amount in the log from latest to earliest timestamp until the quantity is more or equal in the log to the current quantity on the location. But I'm not sure how to do that exactly.
Pseudocode
Quantity on location = 20
Row 1 in log: 5.
20 - 5 = 15 remaining.
Row 2 in log: 5.
15 - 5 = 10 remaining.
Row 3 in log: 5.
10 - 5 = 5 remaining.
Row 4 in log: 5.
5 - 5 = 0 remaining.
Then stop and return the rows.
Edit 1: Expected results and what I have
Location quantity
LOCATION QUANTITY
Loc_1 20
Loc_2 10
SELECT BS.LOCATION, TL.PALL_NUM, BS.QUANTITY
FROM BATCH_STOCK BS
LEFT OUTER JOIN TRANS_LOG TL
ON TL.ITEM_NUM = BS.ITEM_NUM AND LOC_TO = BS.LOCATION
ORDER BY BS.LOCATION
Result
LOCATION PALL_NUM QUANTITY
Loc_2 16 5
Loc_2 15 5
Loc_2 14 5
Loc_1 13 5
Loc_1 12 5
Loc_1 11 5
Loc_1 10 5
... ...
Loc_1 1 5
This is just an extended version from the first question. But shows the output I would get if I didn't count backwards in the log. So for example Loc_2 would only have 2 pallets with quantity still on them but return 3 pallets because 3 pallets have been moved to that location. And Loc_1 would return 13 pallets but the first 9 have already been "picked" so they aren't there anymore.
Expected result
LOCATION PALL_NUM
Loc_2 16
Loc_2 15
Loc_1 13
Loc_1 12
Loc_1 11
Loc_1 10
Edit 2: Table structure
BATCH_STOCK
LOCATION = PK NVARCHAR(13) NOT NULL
QTY = NUMERIC (9,2) NOT NULL
TRANS_LOG
PALL_NUM = BIGINT NOT NULL
LOC_TO = NVARCHAR(16) NULL
QTY = NUMERIC(9,2) NULL

You're looking for a running total? This will return you the 4 rows:
select * from (
select *,
sum(QUANTITY) over (order by PALLET_NUMBER desc) as Quant
from yourtable
) X where Quant <= 20
That only works with full pallets, if you need to include rows for partial quantities, this should work then:
select * from (
select *, isnull(lag(Quant) over (order by PALLET_NUMBER desc),0) as NextQuant
from (
select *,
sum(QUANTITY) over (order by PALLET_NUMBER desc) as Quant
from yourtable
) X
) Y where NextQuant < 15
Update, added examples with the new tables included in the question
select * from (
select *,
sum(QUANTITY) over (partition by LOCATION order by PALL_NUM desc) as Quant
from #TRANS_LOG
) X join #BATCH_STOCK S on S.QUANTITY >= X.Quant and S.LOCATION = X.LOCATION
select * from (
select *, isnull(lag(Quant) over (partition by LOCATION order by PALL_NUM desc),0) as NextQuant
from (
select *,
sum(QUANTITY) over (partition by LOCATION order by PALL_NUM desc) as Quant
from #TRANS_LOG
) X
) Y join #BATCH_STOCK S on S.QUANTITY > Y.NextQuant and S.LOCATION = Y.LOCATION
Results:
   LOCATION PALL_NUM QUANTITY Quant LOCATION QUANTITY
1 Loc_1 13 5 5 Loc_1 20
2 Loc_1 12 5 10 Loc_1 20
3 Loc_1 11 5 15 Loc_1 20
4 Loc_1 10 5 20 Loc_1 20
5 Loc_2 16 5 5 Loc_2 10
6 Loc_2 15 5 10 Loc_2 10
   LOCATION PALL_NUM QUANTITY Quant NextQuant LOCATION QUANTITY
1 Loc_1 13 5 5 0 Loc_1 20
2 Loc_1 12 5 10 5 Loc_1 20
3 Loc_1 11 5 15 10 Loc_1 20
4 Loc_1 10 5 20 15 Loc_1 20
5 Loc_2 16 5 5 0 Loc_2 10
6 Loc_2 15 5 10 5 Loc_2 10
Example that I made

Okay, Here is what i have tried so far. I have used recursive CTE to achieve the expected output.
Schema
CREATE TABLE #tLocationQty
(
Location VARCHAR(10),
Quantity int
)
INSERT INTO #tLocationQty VALUES('Loc_1',20)
INSERT INTO #tLocationQty VALUES('Loc_2',10)
Second table schema
CREATE TABLE #tLog
(
MoveToLocation VARCHAR(10),
Quantity int,
PALL_NUM int
)
INSERT INTO #tLog VALUES('Loc_1',5, 13)
INSERT INTO #tLog VALUES('Loc_1',5, 12)
INSERT INTO #tLog VALUES('Loc_1',5, 11)
INSERT INTO #tLog VALUES('Loc_1',5, 10)
INSERT INTO #tLog VALUES('Loc_1',5, 9)
INSERT INTO #tLog VALUES('Loc_2',5, 16)
INSERT INTO #tLog VALUES('Loc_2',5, 15)
and below is the query.
Note: - I have not use order by clause. You have to use it as per your requirement.
Query
;WITH myCTE
AS
(
SELECT Location, Quantity AS Qty FROM #tLocationQty
UNION ALL
SELECT MoveToLocation, (Qty - t.Quantity) AS Qty FROM #tLog t
INNER JOIN myCTE
ON myCTE.Location = t.MoveToLocation
WHERE myCTE.Qty > 0
),
DistLocation
AS
(
SELECT DISTINCT Location FROM myCTE WHERE qty > 0
),
RowCnt
AS
(
SELECT Location, COUNT(DISTINCT Qty) AS Cnt FROM myCTE WHERE qty > 0
GROUP BY Location
)
--SELECT * FROM RowCnt
SELECT * FROM DistLocation
CROSS APPLY
(
SELECT TOP (SELECT top 1 Cnt FROM RowCnt r WHERE r.location = location)
PALL_NUM
FROM #tLog t
WHERE t.MoveToLocation = location
) c
Output
Location PALL_NUM
Loc_1 13
Loc_1 12
Loc_1 11
Loc_1 10
Loc_2 15
Loc_2 16

Related

How to Group Items in a Count statement

I'm trying to create a query that will return Total Claims reported in 0-3 days, 4-7 days, 8-14 days, and 15+
Select DATEDiff(DD,LossDate,DateReported) As TimeToReport,Count(ClaimId) As Num from LossRun
where PolicyNum='1234567890'
And PolTerm='201403'
Group By DATEDiff(DD,LossDate,DateReported)
order by DATEDiff(DD,LossDate,DateReported);
This is what i get
TimeToReport NumofClaims
0 5
1 3
2 1
3 4
4 3
5 2
6 2
7 2
8 1
12 1
13 1
14 2
15 2
48 1
52 1
107 1
121 1
147 1
533 1
Basically i want to see the total for 0-3, 4-7,8-14,and the rest,,,, timeToReport
You can try to use SUM with CASW WHEN
select
SUM(CASW WHEN TimeToReport <= 3 THEN NumofClaims ELSE 0 END) '0~3 day',
SUM(CASW WHEN TimeToReport >= 4 AND TimeToReport <=7 THEN NumofClaims END) '4-7 days',
SUM(CASW WHEN TimeToReport >= 8 AND TimeToReport <=14 THEN NumofClaims ELSE 0 END) '8-14 days',
SUM(CASW WHEN TimeToReport >= 15 THEN NumofClaims ELSE 0 END) '15+ day'
from (
Select DATEDiff(DD,LossDate,DateReported) As TimeToReport,Count(ClaimId) As Num
from LossRun
where PolicyNum='1234567890'
And PolTerm='201403'
Group By DATEDiff(DD,LossDate,DateReported)
) t
The most simple way is going to be by creating your own temp table which includes the min and max for each bucket and then joining to it.
declare #t table (OrderedID int, EmpID int, EffDate date, Salary money)
insert into #t
values
(1,1234,'20150101',1)
,(2,1234,'20160101',2)
,(3,1234,'20170101',8)
,(4,1234,'20180101',15)
,(1,2351,'20150101',17)
,(5,1234,'20190101',4)
,(5,1234,'20190101',2)
,(5,1234,'20190101',9)
declare #Bin table (MinVal int, MaxVal int)
insert into #Bin
values
(1,3)
,(4,6)
,(7,9)
,(10,15)
,(15,20)
,(20,25)
Select
B.MinVal,count(T.EmpID) as EmpsInBin
From #t T
inner join #Bin B on T.Salary between B.MinVal and B.MaxVal
group by B.MinVal
Output
MinVal EmpsInBin
1 3
4 1
7 2
10 1
15 2

SQL update table loop

I have this following query that gets me a small result set
SELECT
LOC, PLAN, FiscalYear, FiscalPeriod, SALES
FROM
#CurrentPrd PrdAg
WHERE
NOT EXISTS (SELECT AGE.ECPLAN
FROM ECPG_BAK AGE
WHERE PrdAg.LOC = AGE.STORE
AND PrdAg.PLAN = AGE.PLAN
AND PrdAg.FiscalYear = AGE.FiscalYear
AND PrdAg.FiscalPeriod = AGE.FiscalPeriod)
The result set looks like this:
LOC PLAN FiscalYear FiscalPeriod SALES
---------------------------------------------------
5 6 2031 5 -0.206232
12 6 2031 5 5.243052
12 8 2020 4 1.699716
12 8 2020 5 1.699716
14 6 2031 5 0.299972
19 6 2031 5 1.549812
19 8 2020 5 20.114116
33 6 2031 5 2.159767
33 8 2020 5 23.796883
34 6 2031 5 1.142360
34 8 2020 5 9.348583
................................................
Then I have this other query that gets me a number that I need to add to the SALES column. For example, the query below, I used fixed loc and plan to come up with a number:
select
(select SALES
from #TOT
where loc = 12 and PLAN = 6) - (select sum(sales)
from #CurrentPrd
where store = 12 and PLAN = 6) as Comp
Let's assume this query above gets me 10, then I need to add it to line 2 of the result set above, making it
LOC PLAN FiscalYear FiscalPeriod SALES
----------------------------------------------
12 6 2031 5 15.243052
My goal is to make it somewhat dynamic and do the whole process in a simple way, so for each LOC and PLAN combination, I would plug those values into the second select to retrieve the correct number to add to SALES, then update #CurrentPrd. Writing the new number to a new temp table is also an option.
I hope I was able to explain what I'm trying to do. Any help would be appreciated.
Thanks.
Without any actual test data, it's hard to say for sure but I think something like the following should work for you...
SELECT
PrdAg.LOC,
PrdAg.[PLAN],
PrdAg.FiscalYear,
PrdAg.FiscalPeriod,
SALES = PrdAg.SALES + (tx.SALES - cpx.SALES)
FROM
#CurrentPrd PrdAg
CROSS APPLY (SELECT TOP 1 T.SALES FROM #TOT T WHERE PrdAg.LOC = T.LOC AND PrdAg.[PLAN] = t.[PLAN]) tx
CROSS APPLY (SELECT SALES = SUM(CP.SALES) FROM #CurrentPrd CP WHERE PrdAg.LOC = CP.LOC AND PrdAg.[PLAN] = CP.[PLAN]) cpx
WHERE
NOT EXISTS (
SELECT 1
FROM
ECPG_BAK AGE
WHERE
PrdAg.LOC = AGE.STORE
AND PrdAg.[PLAN] = AGE.[PLAN]
AND PrdAg.FiscalYear = AGE.FiscalYear
AND PrdAg.FiscalPeriod = AGE.FiscalPeriod
);

Get Unmatched records from the 3 table

I have 3 tables. The first table 'Status_Mapping' has following columns
Status_original Status_Site
accepted Call Verified
duplicate Duplicate Leads
dq DQ
'Lead_transaction' has the columns:
Lead_transaction_id Rate Status
11 0.01 accepted
12 0.02 accepted
13 0.01 newstatus
'Lead_Instance' table:
Lead_Instance_id Lead_transaction_id product_id affiliate_id
1 11 6 10
2 12 7 11
3 13 6 10
What I want to do is get the count(lead_instance_id) and sum(rate) for status which are not present in status_mapping table and should display status as "other", with product_id = 6 and affiliate_id = 10 My End result should be like
Total Sum Status
1 0.01 Other
you can start with this query:
select count(distinct a.Lead_Instance_id), sum(b.Rate)
from
Lead_Instance as a
inner join
Lead_transaction as b
on (a.Lead_transaction_id = b.Lead_transaction_id)
where
b.Status not in (select distinct Status_original from Status_Mapping)
and a.product_id = 6
and a.affiliate_id = 10

Rolling up values in SQL Server

This is the result of my first sql statement:
SELECT
count(*) countQuarter, Hour, Quarter,
ROW_NUMBER() OVER(ORDER BY Hour, Quarter ASC) AS rownum
FROM
(SELECT [ID] ,[simulationID] ,[time],
replace(str(time/3600,len(ltrim(time/3600))+abs(sign(time/359999)-1)) + ':' + str((time/60)%60,2) + ':' + str(time%60,2),' ','0') dtString,
(time/3600) Hour, (time/60)%60 Minute, case when (time/60)%60<15 then 15 when
(time/60)%60<30 then 30 when (time/60)%60<45 then 45 when (time/60)%60<60 then 60 end
Quarter ,[person] ,[link] ,[vehicle] FROM [TEST].[dbo].[evtLinks]
WHERE simulationID=#simulationID) B
GROUP BY Hour, Quarter
which gives the following results:
Count Hour Quarter Rownum
497 0 15 1
842 0 30 2
1033 0 45 3
1120 0 60 4
1235 1 15 5
1267 1 30 6
1267 1 45 7
1267 1 60 8
1267 2 15 9
1267 2 30 10
I desire a result, where the column fullCount is the sum of the Count of the actual row and the next 3!
Count Hour Quarter Rownum Fullcount
497 0 15 1 3492
842 0 30 2 4230
1033 0 45 3 4655
1120 0 60 4 ...
1235 1 15 5
1267 1 30 6
1267 1 45 7
1267 1 60 8
1267 2 15 9
1267 2 30 10
How can this be done with grouping or analytical functions in SQL Server?
For SQL Server 2012, yes this can be done:
declare #t table ([Count] int,[Hour] int,[Quarter] int,Rownum int)
insert into #t([Count],[Hour],[Quarter],Rownum) values
(497 , 0 , 15 , 1 ),
(842 , 0 , 30 , 2 ),
(1033 , 0 , 45 , 3 ),
(1120 , 0 , 60 , 4 ),
(1235 , 1 , 15 , 5 ),
(1267 , 1 , 30 , 6 ),
(1267 , 1 , 45 , 7 ),
(1267 , 1 , 60 , 8 ),
(1267 , 2 , 15 , 9 ),
(1267 , 2 , 30 , 10 )
select *,SUM([Count]) OVER (
ORDER BY rownum
ROWS BETWEEN CURRENT ROW AND
3 FOLLOWING)
from #t
Here I'm using #t as your current result set - you may be able to adapt this into your current query or may have to place your current query in a CTE.
Unfortunately, the ROWS BETWEEN syntax is only valid on 2012 and later.
Tested the logical scenario and it works, but I don't have your data, so in your case it should look roughly like this:
;WITH CTE as (SELECT count(*) countQuarter,Hour,Quarter,
ROW_NUMBER() OVER(ORDER BY Hour, Quarter ASC) AS rownum
FROM
(SELECT [ID] ,[simulationID] ,[time],
replace(str(time/3600,len(ltrim(time/3600))+abs(sign(time/359999)-1)) + ':' + str((time/60)%60,2) + ':' + str(time%60,2),' ','0') dtString,
(time/3600) Hour, (time/60)%60 Minute, case when (time/60)%60<15 then 15 when
(time/60)%60<30 then 30 when (time/60)%60<45 then 45 when (time/60)%60<60 then 60 end
Quarter ,[person] ,[link] ,[vehicle] FROM [TEST].[dbo].[evtLinks]
WHERE simulationID=#simulationID) B
GROUP BY Hour, Quarter)
SELECT *, CA.Fullcount
FROM CTE
CROSS APPLY (SELECT SUM(countQuarter) Fullcount FROM CTE C WHERE C.ID BETWEEN CTE.ID AND CTE.ID+3) CA

How to extract ids of the rows with minimum value in sql

I have the following table
RecordID Group Date Value
----------------------------------------
1 Test 1 1/01/2012 10
2 Test 1 1/01/2012 10
3 Test 1 1/01/2012 20
4 Test 2 1/01/2012 20
5 Test 1 2/01/2012 10
6 Test 2 2/01/2012 30
7 Test 1 2/01/2012 20
8 Test 1 2/01/2012 20
9 Test 2 2/01/2012 20
10 Test 1 3/01/2012 10
11 Test 2 3/01/2012 10
12 Test 2 3/01/2012 20
13 Test 3 3/01/2012 10
14 Test 1 4/01/2012 10
I need to get all RecordIds, where for the same date and same group it has the lowest value and disregard all records for the same date and group that have greater value. So my query needs to group by "date" and "group" and find records with lowest value, that is result should be:
RecordIds: 1, 2, 4, 5, 9, 10, 11, 13, 14
You can use rank in a sub query.
select T.RecordID,
T.[Group],
T.Date,
T.Value
from
(
select RecordID,
[Group],
Date,
Value,
rank() over(partition by [Group], Date order by Value) as rn
from YourTable
) as T
where T.rn = 1
order by T.RecordID
SQL Fiddle

Resources