Can this SQL Server CTE be expressed with a "simpler" basic query? - sql-server-2012

So that I can union all or join it to a larger outer query of some kind?
-- Get purchase order due date for first purchase order after today which completes a cumulative sum satisfying demand which currently exceeds inventory supply
with cte as
(
select
t1.itemcode as itemcode,
t1.dscription as itemname,
t1.quantity as quantity,
(select sum(t6.quantity)
from por1 t6
inner join opor t7 on t6.docentry = t7.docentry
where t7.docduedate <= t2.docduedate
and t7.docduedate > getdate()
and t6.itemcode = 'item001') as totalqty,
t2.docduedate as eta
from
por1 t1
inner join
opor t2 on t1.docentry = t2.docentry
where
t1.itemcode = 'item001'
and t2.docduedate > getdate()
)
select
cte.itemcode, cte.itemname,
convert(char(10), min(cte.eta), 101)
from
cte
where
totalqty > 55 -- I don't want this to be literal, the outer join would supply this as a calculated value
group by
cte.itemcode, cte.itemname
Re the outer query (which isn't included above because I haven't been able to join an outer query to the cte - I don't know that you can), I'd like it to calculate the amount, currently shown as 55, from the outer query and control which itemcodes to return based on a filtered list from an outer query instead of choosing just one literal itemcode (as above), for example.
I made various attempts to get totalqty using a having clause with no success.
Thanks in advance for your help.

Do you want to get the result of what TotalQty is, or do you only want to use it as a condition?
If it's the former, I would say the CTE way is the best. Otherwise, I would reframe it as a WHERE EXISTS clause on the select going into TotalQty, like WHERE EXISTS (SELECT SUM(T6.Quantity) [...] HAVING SUM(T6.Quantity) > 55).

Related

SQL query: SUM(Quantity)

I'm trying to build a simple SQL query for an item availability check. My aim is to query the items whose total/summed quantity exceeds the stock level. This is what my table (simplified example) looks like:
LineNumber
Item
Quantity
StockQuantity
1
Banana
1
1
2
Apple
1
1
3
Strawberry
1
1
4
Banana
1
1
I have tried it with the following query, but it doesn't work:
SELECT
T1.[LineNumber], T1.[Item],
SUM(T1.[Quantity]), T1.[Stockquantity]
FROM
Table T1
WHERE
T1.[Quantity] > T1.[Stockquantity]
GROUP BY
T1.[LineNumber], T1.[Item], T1.[Quantity], T1.[Stockquantity]
Does anyone have any advice or tips for me on how I should do this query so that the total/summed quantities per line are compared with the stock quantity and not just the individual/single quantities per line with the stock quantity?
Thank you very much for your help in advance.
Given your extended information in this post: https://stackoverflow.com/a/71452446/1690217 the query you are looking for is this:
WITH TotalsLookup as (
SELECT T1.[VisOrder] as [VisOrder], T1.[U_position] as [ItemGroup-LineNumber]
, T0.[DocNum] as [DocNumber], T2.[InvntItem], T1.[ItemCode] as [Itemcode]
, T1.[Quantity], T1.[OpenCreQty] as [openQuantity]
, T2.[OnHand] as [StockQuantity]
, SUM(T1.[Quantity]) OVER (PARTITION BY T1.[ItemCode] ORDER BY T1.[VisOrder]) AS RunningTotal
FROM OQUT T0
INNER JOIN QUT1 T1 ON T0.[DocEntry] = T1.[DocEntry]
INNER JOIN OITM T2 ON T1.[ItemCode] = T2.[ItemCode]
)
SELECT VisOrder, [ItemGroup-LineNumber], DocNumber, Itemcode, Quantity, openQuantity, StockQuantity, RunningTotal
FROM TotalsLookup
WHERE DocNumber = '332050' AND [InvntItem] = 'Y' AND RunningTotal > StockQuantity
Original Post
Your example is possibly oversimplified, If StockQuantity is the result of a lookup into an inventory, so that in the example we can say the following:
Total stock on Hand of Bananas is 1
And then if you are expecting LineNumber 4 to be picked up because this line will make the SUM of Bananas greater than the total stock on hand of Bananas... then you need a Running Total of the Item records in this list:
WITH TotalsLookup as (
SELECT
T1.[LineNumber], T1.[Item],
T1.[Quantity], T1.[StockQuantity],
SUM (T1.[Quantity]) OVER (PARTITION BY Item ORDER BY LineNumber) AS RunningTotal
FROM
[Table] T1
)
SELECT *
FROM TotalsLookup
WHERE RunningTotal > StockQuantity;
LineNumber
Item
Quantity
Stockquantity
RunningTotal
4
Banana
1
1
2
See this Fiddle that covers a few variants of this query and will give you a playground to practice: http://sqlfiddle.com/#!18/a95bb/2
The origin of StockQuantity can itself have a significant impact on the performance and other query options that might be viable, I call this out so that you can be mindful of this when you apply this logic into your application domain, if StockQuantity is not a static value, but is itself an aggregate then that may impact how the comparison should be performed.
You can refer to aggregates of a GROUP BY in the having clause
SELECT
T1.[Item],
SUM(T1.[Quantity]) As TotalQuantity,
MAX(T1.[Stockquantity]) AS StockLevel
FROM
Table T1
GROUP BY
T1.[Item]
HAVING
SUM(T1.[Quantity]) > MAX(T1.[Stockquantity])
This assumes that the Stockquantity is the same for all occurrences of a product.
The WHERE clause is executed before grouping, the HAVING clause after grouping.
If you need the line numbers, then you can use the STRING_AGG aggregate function to join the line numbers into a single string per group
SELECT STRING_AGG(T1.[LineNumber], ', ') As LineNumbers, ...
your SQL modified code(SQL code didn't work) that does not provide a single output
SELECT linenumber,
a.item,
quantity,
SumQuantity,
stockquantity
FROM table A
JOIN(SELECT item,
Sum(quantity) SumQuantity,
max(stockquantity) stockquantity
FROM table C
GROUP BY item) B
ON A.item = B.item
WHERE SumQuantity> stockquantity
ouput
linenumber
item
quantity
quantity2
stockquantity
1
Banana
1
2
1
4
Banana
1
2
1
However I use the follwing query
SELECT String_agg(linenumber, ',') LineNumbers,
a.item,
quantity,
SumQuantity,
stockquantity
FROM table A
JOIN(SELECT item,
Sum(quantity) SumQuantity
max(stockquantity) stockquantity
FROM table C
GROUP BY item) B
ON A.item = B.item
WHERE SumQuantity> stockquantity
GROUP BY a.item,
quantity,
SumQuantity,
stockquantity
that bring following result
LineNumbers
Item
Quantity
SumQuantity
StockQuantity
1,4
Banana
1
2
1
Thank you all for your effort and suggestions for solutions. I have tried to incorporate them all. A few of them worked. With others I still have a few problems:
#Chris Schaller
I like you solution a lot! But in my non-simplified, real SQL code, there are item groups that the PARTITION function cannot cope with. My result with your solution is therefore as follows.
However, I will continue to work on it. Thanks!
WITH TotalsLookup as (
SELECT T1.[VisOrder] as 'VisOrder', T1.[U_position] as 'ItemGroup-LineNumber', T0.[DocNum] as 'DocNumber', T1.[ItemCode] as 'Itemcode', T1.[Quantity] as 'Quantity', T1.[OpenCreQty] as 'openQuantity', T2.[OnHand] as 'StockQuantity', SUM(T1.[Quantity]) OVER (PARTITION BY T1.[ItemCode] ORDER BY T1.[VisOrder]) AS RunningTotal
FROM OQUT T0
INNER JOIN QUT1 T1 ON T0.[DocEntry] = T1.[DocEntry]
INNER JOIN OITM T2 ON T1.[ItemCode] = T2.[ItemCode]
WHERE T0.[DocNum] = '332050' AND T2.[InvntItem] = 'Y'
)
SELECT *
FROM TotalsLookup
WHERE RunningTotal > Quantity
Output: here
#RF1991
I have also tried to implement your solution, but it has not really worked so far. I am still looking for the mistake. Thanks for the help!
SELECT T1.[U_position], T1.[ItemCode], T1.[Quantity], T1.[OpenCreQty], T2.[OnHand]
FROM OQUT T0
INNER JOIN QUT1 T1 ON T0.[DocEntry] = T1.[DocEntry]
INNER JOIN OITM T2 ON T1.[ItemCode] = T2.[ItemCode]
JOIN(SELECT T1.[ItemCode], SUM(T1.[Quantity]) as 'SumQuantity', MAX(T2.[OnHand]) as 'Stockquantity' FROM OQUT T0 INNER JOIN QUT1 T1 ON T0.[DocEntry] = T1.[DocEntry] INNER JOIN OITM T2 ON T1.[ItemCode] = T2.[ItemCode] GROUP BY T1.[ItemCode]) B
ON T1.[ItemCode] = B.[ItemCode]
WHERE SumQuantity > stockquantity AND T0.[DocNum] = '332050' AND T2.[InvntItem] = 'Y'
#Oliver Jacot-Descombes
It works. Thank you very much!

Query to get the line with the latest date

SELECT T0.[ItemCode], T0.[PriceFOB], T0.[PriceAtWH], T0.[DocEntry], T0.[CardCode], T2.[DocDate], T3.[CardName] FROM IPF1 T0 LEFT JOIN OIPF T2 ON T2.[DocDate] = (SELECT MAX(T4.[DocDate]) FROM OIPF T4 WHERE T0.[DocEntry]=T4.[DocEntry]) INNER JOIN OCRD T3 ON T0.[CardCode] = T3.[CardCode] WHERE T3.[GroupCode] = '105'
I am using the following query listed above.
The issue rises where I have multiple lines of the same item listed but my goal is to get the one with the LATEST DocDate.
If someone can please help and explain what it is that I am doing wrong.
I have tried Order By, Group By but keep getting multiple errors (syntax mostly)
SELECT ItemCode, PriceAtWH, DocEntry, PriceFOB, CardCode FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY T0.[ItemCode] ORDER BY T0.[DocEntry] DESC) AS ROWNUM FROM IPF1 T0) IPF1 WHERE ROWNUM = 1
This is the other query that I was able to come close to my solution but I need it to filter for only GroupCode = '105' from OCRD matching the CardCode
The Main goal is to filter out a Master DATA which is combined with IPF1 and OCRD(Only need to pull Group Code from this) and to filter the IPF1 with the Group Code first. Then proceed to do the Max Date or Max DocEntry ID for each item.
You could use a distinct to get one row per event even if they have different dates and use the MAX on the select
SELECT distinct T0.[ItemCode],
T0.[PriceFOB],
T0.[PriceAtWH],
T0.[DocEntry],
T0.[CardCode],
T3.[CardName],
MAX(T2.[DocDate] ) DocDate
FROM IPF1 T0
LEFT JOIN OIPF T2
ON T0.[DocEntry]=T2.DocEntry]
INNER JOIN OCRD T3
ON T0.[CardCode] = T3.[CardCode]
WHERE T3.[GroupCode] = '105'
GROUP BY T0.[ItemCode], T0.[PriceFOB], T0.[PriceAtWH], T0.[DocEntry], T0.[CardCode],T3.[CardName]
WITH T AS (SELECT T10.[DocEntry], T10.[CardCode], T10.[ItemCode], T10.[PriceFOB], T10.[PriceAtWH] FROM IPF1 T10 INNER JOIN OCRD T11 ON T10.[CardCode] = T11.[CardCode] WHERE T11.[GroupCode] = '105')
SELECT T.[DocEntry], T.[CardCode], T.[ItemCode], T.[PriceFOB], T.[PriceAtWH] FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY T.[ItemCode] ORDER BY T.[DocEntry] DESC) AS ROWNUM FROM T) T WHERE ROWNUM = 1
So I ended up solving my own problem.
Thank you Julissa for trying buy your query has an error where you are missing a "[" in front of DocEntry and also the query does not do what I want it do, multiple instances of the same item for all date still show up. Thank you for trying!

items are repeated in SAP B1 query

Below query is giving me correct output on quantity (but have to add quantities of repeated styles) but item code and item description is repeated. i want to see one time code having Total PO qty and what in stock.
Can you please advise what's wrong with this query.
Thanks
Shahzad Ahmed enter image description here
SELECT
T1.[ItemCode],
T1.[Dscription],
sum(T1.[Quantity]) as 'PO QTY',
T3.[OnHand]
FROM
ORDR T0 INNER JOIN RDR1 T1 ON T0.[DocEntry] = T1.[DocEntry]
INNER JOIN OITM T2 ON T1.[ItemCode] = T2.[ItemCode]
INNER JOIN OITW T3 ON T2.[ItemCode] = T3.[ItemCode]
WHERE
T0.[CardCode] = 'c00192' and
T0.[NumAtCard] Like '818276%%' and
T3.[WhsCode] = '161'
GROUP BY T1.[ItemCode], T1.[Dscription], T1.[Quantity], T3.[OnHand]
You just need to remove T1.[Quantity] from your GROUP BY. You should read on how to use a Group by, but to summarize, when you're calling a group by, you're asking to look for rows with the same values in some columns.
For exemple, here you want to add the quantities of every row with the same ItemCode, Dscription, OnHand, but you're asking to also look for rows with the same Quantity here.
Here is a good article on GROUP BY

Solving SQL Query Decision making sql

I have a problem
I need to select all my contoNames from Conto which was active between FromDate And Todate
And If i dont get a match in that join i need either to set a NULL or do something else
But i have read that when using AND in your join expression it reads the And statement before it does the join and
it works like and Where statement filtering data away.
So i dont get a NULL
In this example
-- This statement gives 0 rows there is no contractId in that time span
SELECT S.Name, C.ContoName, C.fromDate, C.Todate
From Sales
Left outer Sales S on S.ContractId = C.ContractId AND '2014-12-31' BETWEEN c.fromDate AND C.Todate
This what i want to achive either to get a NULL value in my left join or do somekind of decision flow
if no contractId in Timespan then
Only join on contractId
SELECT S.Name, C.ContoName, C.fromDate, C.Todate
From Sales
INNER JOIN Sales S on S.ContractId = C.ContractId AND ( '2014-12-31'BETWEEN c.fromDate AND C.Todate OR Sales.ContractId = Conto.ContractId )
Not Realistic the AND operator works like a where statement and filtering data before it does the JOIN
SELECT S.Name, coalesce(C.ContoName,C1.ContoName)contoName,C.fromDate,C.Todate
From Sales S
LEFT JOIN Conto C on S.ContractId = C.ContractId
AND '2014-12-31' >BETWEEN c.fromDate AND C.Todate )
LEFT JOIN Conto C1 on S.ContractId = C1.contractId
Does anyone have and good idea to solve this in a nice way using tsql or standard sql
if no contractId in Timespan then Only join on contractId
I am reading this to mean "If there are contractIDs in the Timespan, then only show those. If there are none in the timespan, then show the ones that aren't in the timespan."
If I'm reading that wrong, then you need to clarify your question.
If I'm right, then you handle this with either a CASE or, as I will show, with an (AND) OR (AND) structure:
SELECT S.Name, C.ContoName,C.fromDate,C.Todate
From Sales S
LEFT JOIN Conto C
ON (
S.ContractId = C.ContractId
AND '2014-12-31' BETWEEN c.fromDate AND C.Todate
) OR (
S.ContractId = C.ContractId
AND NOT EXISTS(
SELECT * FROM Conto c1
WHERE S.ContractId = c1.ContractId
AND '2014-12-31' BETWEEN c1.fromDate AND c1.Todate
)
)

Inner join that ignore singlets

I have to do an self join on a table. I am trying to return a list of several columns to see how many of each type of drug test was performed on same day (MM/DD/YYYY) in which there were at least two tests done and at least one of which resulted in a result code of 'UN'.
I am joining other tables to get the information as below. The problem is I do not quite understand how to exclude someone who has a single result row in which they did have a 'UN' result on a day but did not have any other tests that day.
Query Results (Columns)
County, DrugTestID, ID, Name, CollectionDate, DrugTestType, Results, Count(DrugTestType)
I have several rows for ID 12345 which are correct. But ID 12346 is a single row of which is showing they had a row result of count (1). They had a result of 'UN' on this day but they did not have any other tests that day. I want to exclude this.
I tried the following query
select
c.desc as 'County',
dt.pid as 'PID',
dt.id as 'DrugTestID',
p.id as 'ID',
bio.FullName as 'Participant',
CONVERT(varchar, dt.CollectionDate, 101) as 'CollectionDate',
dtt.desc as 'Drug Test Type',
dt.result as Result,
COUNT(dt.dru_drug_test_type) as 'Count Of Test Type'
from
dbo.Test as dt with (nolock)
join dbo.History as h on dt.pid = h.id
join dbo.Participant as p on h.pid = p.id
join BioData as bio on bio.id = p.id
join County as c with (nolock) on p.CountyCode = c.code
join DrugTestType as dtt with (nolock) on dt.DrugTestType = dtt.code
inner join
(
select distinct
dt2.pid,
CONVERT(varchar, dt2.CollectionDate, 101) as 'CollectionDate'
from
dbo.DrugTest as dt2 with (nolock)
join dbo.History as h2 on dt2.pid = h2.id
join dbo.Participant as p2 on h2.pid = p2.id
where
dt2.result = 'UN'
and dt2.CollectionDate between '11-01-2011' and '10-31-2012'
and p2.DrugCourtType = 'AD'
) as derived
on dt.pid = derived.pid
and convert(varchar, dt.CollectionDate, 101) = convert(varchar, derived.CollectionDate, 101)
group by
c.desc, dt.pid, p.id, dt.id, bio.fullname, dt.CollectionDate, dtt.desc, dt.result
order by
c.desc ASC, Participant ASC, dt.CollectionDate ASC
This is a little complicated because the your query has a separate row for each test. You need to use window/analytic functions to get the information you want. These allow you to do calculate aggregation functions, but to put the values on each line.
The following query starts with your query. It then calculates the number of UN results on each date for each participant and the total number of tests. It applies the appropriate filter to get what you want:
with base as (<your query here>)
select b.*
from (select b.*,
sum(isUN) over (partition by Participant, CollectionDate) as NumUNs,
count(*) over (partition by Partitipant, CollectionDate) as NumTests
from (select b.*,
(case when result = 'UN' then 1 else 0 end) as IsUN
from base
) b
) b
where NumUNs <> 1 or NumTests <> 1
Without the with clause or window functions, you can create a particularly ugly query to do the same thing:
select b.*
from (<your query>) b join
(select Participant, CollectionDate, count(*) as NumTests,
sum(case when result = 'UN' then 1 else 0 end) as NumUNs
from (<your query>) b
group by Participant, CollectionDate
) bsum
on b.Participant = bsum.Participant and
b.CollectionDate = bsum.CollectionDate
where NumUNs <> 1 or NumTests <> 1
If I understand the problem, the basic pattern for this sort of query is simply to include negating or exclusionary conditions in your join. I.E., self-join where columnA matches, but columns B and C do not:
select
[columns]
from
table t1
join table t2 on (
t1.NonPkId = t2.NonPkId
and t1.PkId != t2.PkId
and t1.category != t2.category
)
Put the conditions in the WHERE clause if it benchmarks better:
select
[columns]
from
table t1
join table t2 on (
t1.NonPkId = t2.NonPkId
)
where
t1.PkId != t2.PkId
and t1.category != t2.category
And it's often easiest to start with the self-join, treating it as a "base table" on which to join all related information:
select
[columns]
from
(select
[columns]
from
table t1
join table t2 on (
t1.NonPkId = t2.NonPkId
)
where
t1.PkId != t2.PkId
and t1.category != t2.category
) bt
join [othertable] on (<whatever>)
join [othertable] on (<whatever>)
join [othertable] on (<whatever>)
This can allow you to focus on getting that self-join right, without interference from other tables.