How to split row value according to specific column? - sql-server

I have Table A and Table B and I'm having trouble getting the fulfilled qty in Table B where sum up of Fulfilled qty is equal to Available field in Table A.
For Item ID 1, the available qty is 99 in Table A and in Table B this Item ID 1 have different rack, with 99 qty, it only able to fulfill 60 qty for Rack A and remaining 39 qty for Rack B.
Table A
ID Available
1 99
2 5
Table B
ID Rack Required
1 A 60
1 B 102
1 C 8
2 A 10
Desired Results
ID Rack Required Fulfilled
1 A 60 60
1 B 102 39
1 C 8 0
2 A 4 4
2 B 2 1
I have tried using query below but seems not able to get the expected results
SELECT ID,
RACK,
REQUIREDQTY,
SUM(FULFILLEDQTY) OVER (ORDER BY ID,
RACK,
rows between unbounded preceding and 1 preceding) AS FULFILLEDQTY
FROM TABLEB
LEFT JOIN TABLEA ON TABLEB.ID = TABLEA.ID;

use sum(Required) over (...) and check against Available
select A.ID, B.Rack, B.Required,
Fulfilled = case when sum(B.Required) over (partition by A.ID order by B.Rack)
<= A.Available
then B.Required
when sum(B.Required) over (partition by A.ID order by B.Rack)
- B.Required <= A.Available
then A.Available
- sum(B.Required) over (partition by A.ID order by B.Rack)
+ B.Required
else 0
end
from A
inner join B on A.ID = B.ID
order by A.ID, B.Rack
db<>fiddle demo
EDIT : due to change of structure (additional column rack)
select A.ID, A.rack, B.bin, B.Required,
Fulfilled = case when sum(B.Required) over (partition by A.ID, A.rack
order by B.bin)
<= A.Available
then B.Required
when sum(B.Required) over (partition by A.ID, A.rack
order by B.bin)
- B.Required
<= A.Available
then A.Available
- sum(B.Required) over (partition by A.ID, A.rack
order by B.bin)
+ B.Required
else 0
end
from A
inner join B on A.ID = B.ID and A.rack = B.rack
order by A.ID, A.rack, B.bin

Related

Recurcive cte to identify circular reference in data

I am trying to identify recursive/circular reference in my data for which I need recursive cte.
For example I have table that contains Product_ID and Inner_Product_ID. I want results when Product_ID A is inner to Product_ID B, which is inner to Product_ID C, which is inner to Product_ID A.
Sample data
PRODUCT_ID INNER_PRODUCT_ID
12 36
24 12
36 24
1 2
3 4
Expected output
PRODUCT_ID INNER_PRODUCT_ID
12 36
24 12
36 24
I have tried basic query with cte but not sure how to implement recursive cte for this problem:
;WITH RNCTE
AS ( SELECT *,
ROW_NUMBER() OVER (PARTITION BY pr1.PRODUCT_ID
ORDER BY pr1.PRODUCT_ID
) rn
FROM
TableName pr1),
cte
AS ( SELECT *
FROM RNCTE
WHERE RNCTE.rn = 1
UNION ALL
SELECT *
FROM cte c
JOIN RNCTE r
ON r.PRODUCT_ID = c.PRODUCT_ID
AND r.rn = c.rn + 1)
SELECT *
FROM cte;
try this - it walks through the linked records, and finds if the 'walk' eventually terminates, or not. If it lasts for more than the number of records in the table, then it must be a loop. 'Efficient' I am not sure of that!
;WITH UCNT AS (SELECT count(0) c from products),
RNCTE
AS (SELECT 1 as Levle, Product_ID, INNER_PRODUCT_ID FROM Products
UNION ALL
SELECT levle + 1, P.Product_ID, P.INNER_PRODUCT_ID
FROM RNCTE R
JOIN Products P
ON P.PRODUCT_ID = R.INNER_PRODUCT_ID
WHERE levle <= (SELECT c + 2 FROM UCNT))
--when the recursion count levle exceeds the count of records in the table,
--we must have recursion, because
--termination has to otherwise occur. The most extreme case is
--that all records are linked, with termination
--after this, we have to be in a 'loop'
SELECT TOP 1 with ties * FROM RNCTE order by levle desc
option (maxrecursion 0)
I think you don't need to use CTE or RECUSRIVE CTE :
SELECT pr1.*
FROM TableName pr1
WHERE EXISTS (SELECT 1 FROM TableName pr2 WHERE pr2.INNER_PRODUCT_ID = pr1.PRODUCT_ID);

SQL - get all rows in one table created in a time difference of 1 minute

I have a table as:
Id Ticket_NUM Date Comments
== ========== ======================== =======
1 2 2014-08-29 08:44:34.122 a
2 5 2014-08-29 08:44:34.125 b
3 3 2014-08-29 08:44:34.137 a
4 4 2016-08-29 08:44:34.137 b
Now, I would like to get Ticket_NUM that are created on the same datetime(2014-08-29 08:44:34.122) with a maximum time difference of < 60 seconds. Can anyone let me know how can we write a query to get this data. I used self join on the table but I'm not getting Ticket_NUM(2, 5, 3) that I'm looking for.
One way would be with LEAD and LAG
select * from(
select
*,
myFlag = case
when abs(datediff(second,[date],lag([Date]) over (order by [Date]))) < 60 then 1
when abs(datediff(second,[date],lead([Date]) over (order by [Date]))) < 60 then 1
else 0
end
from yourtable)
where myFlag = 1
order by [Date]
You can remove the where myFlag = 1 to see which ones are flagged.
Or an uglier method:
with cte as(
select *, RN = row_number() over (order by [Date])
from yourtable
),
staging as(
select
c.ID
,c.Ticket_NUM
,c.[Date]
,c.Comments
,c2DT = min(c2.[Date])
,c3DT = max(c3.[Date])
from
cte c
left join cte c2 on c2.RN = c.RN + 1 --row above
left join cte c3 on c3.RN = c.RN - 1 --row below
group by
c.ID
,c.Ticket_NUM
,c.[Date]
,c.Comments)
select
c.ID
,c.Ticket_NUM
,c.[Date]
,c.Comments
from stating c
where abs(datediff(second,c.[Date],c.c2DT)) < 60 or abs(datediff(second,c.[Date],c.c3DT)) < 60

SQL Server multiple select on an Offset...fetch...next query

I'm trying to get data into datatables (js library for data table) by server-side processing.
The data should be produced as below
+---------+--------+--------+
| Name | TotalA | TotalB |
+---------+--------+--------+
| Person1 | 10 | 40 |
+---------+--------+--------+
The query that I tried
select
a.Name,
(select count(*) from SummaryA where id = a.id) as TotalA,
(select count(*) from SummaryB where id = a.id) as TotalB
from
records a
order by
a.Name
offset 0 rows fetch next 10 rows only
and
select
aa.Name,
(select count(*) from SummaryA where id = aa.id) as TotalA,
(select count(*) from SummaryB where id = aa.id) as TotalB
from
(select
a.Name, a.id
from
records a
order by
a.Name
offset 0 rows fetch next 10 rows only) as aa
However, these queries will result in an error as below
Error in query: Invalid usage of the option NEXT in the FETCH statement.
Running below query is not a problem
select
a.Name
from
records a
offset 0 rows fetch next 10 rows only
Issue- offset_row_count_expression can be a variable, parameter, or constant scalar subquery. When a subquery is used, it cannot reference any columns defined in the outer query scope.
link
Try
;with temp as (select a.name ,
count(b.id) as TotalA ,
count(c.id) as Totalb
FROM records a
left join SummaryA b
b.id=a.id
left join SummaryB c
c.id=a.id
group by a.name)
select * from temp
order by temp.Name
Offset 0 rows
fetch next 10 rows only
This can also be solved
with tmp as (
select a.name ,
a.id
FROM records a
order by temp.Name
Offset 0 rows
fetch next 10 rows only
)
select a.name ,
count(b.id) as TotalA ,
count(c.id) as Totalb
FROM tmp a
left join SummaryA b
b.id=a.id
left join SummaryB c
c.id=a.id
group by a.name order by a.Name

SQL Server - Select most recent records with condition

I have a table like this.
Table :
ID EnrollDate ExitDate
1 4/1/16 8/30/16
2 1/1/16 null
2 1/1/16 7/3/16
3 2/1/16 8/1/16
3 2/1/16 9/1/16
4 1/1/16 12/12/16
4 1/1/16 12/12/16
4 1/1/16 12/12/16
4 1/1/16 null
5 5/1/16 11/12/16
5 5/1/16 11/12/16
5 5/1/16 11/12/16
Need to select the most recent records with these conditions.
One and only one record has the most recent enroll date - select that
Two or more share same most recent enroll date and one and only one record has either a NULL Exit Date or the most recent Exit Date - Select the record with null. If no null record pick the record with recent exit date
Two or more with same enroll and Exit Date - If this case exists, don't select those record
So the expected result for the above table should be :
ID EnrollDate ExitDate
1 4/1/16 8/30/16
2 1/1/16 null
3 2/1/16 9/1/16
4 1/1/16 null
I wrote the query with group by. I am not sure how to select with the conditions 2 and 3.
select t1.* from table t1
INNER JOIN(SELECT Id,MAX(EnrollDate) maxentrydate
FROM table
GROUP BY Id)t2 ON EnrollDate = t2.maxentrydate and t1.Id=t2.Id
Please let me know what is the best way to do this.
Using the rank() window function, I think it's possible.
This is untested, but it should work:
select t.ID, t.EnrollDate, t.ExitDate
from (select t.*,
rank() over(
partition by ID
order by EnrollDate desc,
case when ExitDate is null then 1 else 2 end,
ExitDate desc) as rnk
from tbl t) t
where t.rnk = 1
group by t.ID, t.EnrollDate, t.ExitDate
having count(*) = 1
The basic idea is that the rank() window function will rank the most "recent" rows with a value of 1, which we filter on in the outer query's where clause.
If more than one row have the same "most recent" data, they will all share the same rank of 1, but will get filtered out by the having count(*) = 1 clause.
Use ROW_NUMBER coupled with CASE expression to achieve the desired result:
WITH Cte AS(
SELECT t.*,
ROW_NUMBER() OVER(
PARTITION BY t.ID
ORDER BY
t.EnrollDate DESC,
CASE WHEN t.ExitDate IS NULL THEN 0 ELSE 1 END,
t.ExitDate DESC
) AS rn
FROM Tbl t
INNER JOIN (
SELECT
ID,
COUNT(DISTINCT CHECKSUM(EnrollDate, ExitDate)) AS DistinctCnt, -- Count distinct combination of EnrollDate and ExitDate per ID
COUNT(*) AS RowCnt -- Count number of rows per ID
FROM Tbl
GROUP BY ID
) a
ON t.ID = a.ID
WHERE
(a.DistinctCnt = 1 AND a.RowCnt = 1)
OR a.DistinctCnt > 1
)
SELECT
ID, EnrollDate, ExitDate
FROM Cte c
WHERE Rn = 1
The ORDER BY clause in the ROW_NUMBER takes care of conditions 2 and 3.
The INNER JOIN and the WHERE clause take care of 1 and 4.
ONLINE DEMO
with B as (
select id, enrolldate ,
exitdate,
row_number() over (partition by id order by enrolldate desc, case when exitdate is null then 0 else 1 end, exitdate desc) rn
from ab )
select b1.id, b1.enrolldate, b1.exitdate from b b1
left join b b2
on b1.rn = b2.rn -1 and
b1.id = b2.id and
b1.exitdate = b2.exitdate and
b1.enrolldate = b2.enrolldate
where b1.rn = 1 and
b2.id is nULL
The left join is used to fullfill the 3) requirement. When record is returned then we don't want it.

MSSql only group those with count greater than 3 and return the rest records

I want to group the key with count greater than 3, and the query will return the rest of the records also. I don't want to use Union All, is there any other way to do it?
ID
1
1
1
2
3
3
4
4
4
4
Return
1
1
1
2
3
3
4
You can use ranking- and aggregate functions:
WITH CTE AS
(
SELECT ID,
CNT = COUNT(*) OVER (PARTITION BY ID),
RN = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID)
FROM dbo.TableName
)
SELECT ID
FROM CTE
WHERE CNT <= 3 OR RN = 1
Demo
I'd do it like this
SELECT
GroupedData.ID
FROM
(SELECT ID, CNT = COUNT(*)
FROM dbo.TableName
GROUP BY ID) GroupedData AS g
LEFT JOIN dbo.TableName AS t
ON t.id = g.id and g.CNT<=3
This also allows you to add further columns which report details for the group or individual record as appropriate
SELECT
g.ID,
ISNULL(t.RecordName,'Grouped Records') as RecordName,
ISNULL(t.NumericField,g.NumericField) as NumericField
FROM
(
SELECT ID, CNT = COUNT(*), SUM(NumericField) as NumericField
FROM dbo.TableName
GROUP BY ID
) GroupedData AS g
LEFT JOIN dbo.TableName AS t
ON t.id = g.id and g.CNT<=3

Resources