SQL to find inventory - sql

I am very new to SQL and I have a the following situation:
BTW, "qoh" = "quantity on hand"
product table:
prod_code
qoh
11QER/31
8
13-Q2/P2
32
14-Q1/L3
18
1546-QQ2
15
1558-QW1
23
2232/QTY
8
2232/QWE
6
2238/QPD
12
23109-HB
23
23114-AA
8
54778-2T
43
89-WRE-Q
11
PVC23DRT
188
SM-18277
172
SW-23116
237
WR3/TT3
18
invoiced detail table:
prod_code
quantity-sold
13-Q2/P2
1
23109-HB
1
54778-2T
2
2238/QPD
1
1546-QQ2
1
13-Q2/P2
5
54778-2T
3
23109-HB
2
PVC23DRT
12
SM-18277
3
2232/QTY
1
23109-HB
1
89-WRE-Q
1
13-Q2/P2
2
54778-2T
1
PVC23DRT
5
WR3/TT3
3
23109-HB
1
I want to write a SQL to find out the current inventory, QOH in the product table minus the related item quantity-sold:
prod_code
quantity-sold
13-Q2/P2
0 (8 - 1 - 5 -2)
54778-2T
37 (43 - 2 - 3 - 1)
I tried to use join tables etc and the results did not come out right.
How to write the SQL?
Much appreciated!
Philip

A correlated subquery is a simple what to write this:
select p.*,
p.qoh - (select coalesce(sum(id.quantity_sold), 0)
from invoice_detail id
where id.prod_code = p.prod_code
) as current_inventory
from product p;
It is not clear why your result set has only two products. This version would include all products. And with in index on invoice_detail(prod_code, quantity_sold) should be the fastest method to do what you want.

SELECT
product.prod_code,
prod_code.qoh,
COALESCE( qtySold.total_sold, 0 ) AS qty_sold,
( prod_code.qoh - COALESCE( qtySold.total_sold, 0 ) ) AS current_inventory
FROM
product
LEFT OUTER JOIN
(
SELECT
prod_code,
SUM( quantity_sold ) AS total_sold
FROM
invoiced_detail
GROUP BY
prod_code
) AS qtySold ON
product.prod_code = qtySold.prod_code
ORDER BY
product.prod_code

A left join may be used if you summarized the total quantity sold first and used this as a subquery to join on. See example with your sample data below:
Example
Schema (SQLite v3.30)
CREATE TABLE products (
`prod_code` VARCHAR(8),
`qoh` INTEGER
);
INSERT INTO products
(`prod_code`, `qoh`)
VALUES
('11QER/31', '8'),
('13-Q2/P2', '32'),
('14-Q1/L3', '18'),
('1546-QQ2', '15'),
('1558-QW1', '23'),
('2232/QTY', '8'),
('2232/QWE', '6'),
('2238/QPD', '12'),
('23109-HB', '23'),
('23114-AA', '8'),
('54778-2T', '43'),
('89-WRE-Q', '11'),
('PVC23DRT', '188'),
('SM-18277', '172'),
('SW-23116', '237'),
('WR3/TT3', '18');
CREATE TABLE invoice_details (
`prod_code` VARCHAR(8),
`quantity-sold` INTEGER
);
INSERT INTO invoice_details
(`prod_code`, `quantity-sold`)
VALUES
('13-Q2/P2', '1'),
('23109-HB', '1'),
('54778-2T', '2'),
('2238/QPD', '1'),
('1546-QQ2', '1'),
('13-Q2/P2', '5'),
('54778-2T', '3'),
('23109-HB', '2'),
('PVC23DRT', '12'),
('SM-18277', '3'),
('2232/QTY', '1'),
('23109-HB', '1'),
('89-WRE-Q', '1'),
('13-Q2/P2', '2'),
('54778-2T', '1'),
('PVC23DRT', '5'),
('WR3/TT3', '3'),
('23109-HB', '1');
Query #1
SELECT
p.prod_code,
p.qoh - IFNULL( t1.total_quantity_sold,0) as current_inventory
FROM
products p
LEFT JOIN
(SELECT
id.`prod_code`,
sum(id.`quantity-sold`) as total_quantity_sold
FROM
invoice_details id
GROUP BY
id.prod_code
) t1 ON p.prod_code = t1.prod_code;
prod_code
current_inventory
11QER/31
8
13-Q2/P2
24
14-Q1/L3
18
1546-QQ2
14
1558-QW1
23
2232/QTY
7
2232/QWE
6
2238/QPD
11
23109-HB
18
23114-AA
8
54778-2T
37
89-WRE-Q
10
PVC23DRT
171
SM-18277
169
SW-23116
237
WR3/TT3
15
View on DB Fiddle

Related

Fetch conditional rows in SQL server

I need a query like below. ApplicationID and InvoiceNumber columns show purchases made. Negative values in the Revenue rows indicate shopping refund. The ApplicationID column does not change when the purchase is refunded, but the InvoiceNumber column changes for the refund. I determine the returns according to the price totals of different InvoiceNumbers in the same ApplicationID equal to zero. For example, customer A bought 4 products that InvoiceNumber=AA in ApplicationID=11 shopping, but refund 2 of them (InvoiceNumber=BB). I want to get the remaining rows after the refunds are extracted. So in this example, rows 1-2 and 5-6 will eliminate each other for ApplicationID=11 and only rows 3-4 will remain. In addition, ApplicationID=22 and ApplicationID=33 rows will also come as it does not contain refunds. Finally, rows 3,4,7, 8 and 9 will get. How do I do this?
CustomerCode ApplicationID InvoiceNumber Date Revenue
A 11 AA 1.01.2020 150
A 11 AA 2.01.2020 200
A 11 AA 1.01.2020 250
A 11 AA 1.01.2020 300
A 11 BB 5.01.2020 -150
A 11 BB 5.01.2020 -200
A 22 CC 7.02.2020 500
A 22 DD 7.02.2020 700
A 11 AA 2.01.2020 800
I wrote the result I want. I want to subtract zero sum of revenue according to CustomerCode and ApplicationID and fetch all other columns
example code:
select a.CustomerCode,a.ApplicationID from Table a
group by CustomerCode,a.ApplicationID
having SUM(Revenue)>0
My desired result:
CustomerCode ApplicationID InvoiceNumber Date Revenue
A 11 AA 1.01.2020 250
A 11 AA 1.01.2020 300
A 22 CC 7.02.2020 500
A 22 DD 7.02.2020 700
A 11 AA 2.01.2020 800
I think you've gone down a route of needing to sum your results to remove certain rows from your data but that's not necessarily the case.
You can use a LEFT JOIN back to itself joining on CustomerCode, ApplicationID and Revenue = -Revenue; this effectively finds "purchase" rows that have an associated "refund" row (and vice versa). You can then just filter them off with your WHERE clause
Here's the code I used
DROP TABLE IF EXISTS #Orders
CREATE TABLE #Orders (CustomerCode VARCHAR(1), ApplicationID INT, InvoiceNumber VARCHAR(2), [Date] DATE, Revenue INT)
INSERT INTO #Orders (CustomerCode, ApplicationID, InvoiceNumber, Date, Revenue)
VALUES ('A', 11, 'AA', '2020-01-01', 150),
('A', 11, 'AA', '2020-01-02', 200),
('A', 11, 'AA', '2020-01-01', 250),
('A', 11, 'AA', '2020-01-01', 300),
('A', 11, 'BB', '2020-01-05', -150),
('A', 11, 'BB', '2020-01-05', -200),
('A', 22, 'CC', '2020-01-07', 500),
('A', 22, 'DD', '2020-01-07', 700),
('A', 11, 'AA', '2020-01-02', 800)
SELECT O.CustomerCode, O.ApplicationID, O.InvoiceNumber, O.Date, O.Revenue
FROM #Orders AS O
LEFT JOIN #Orders AS O2 ON O2.ApplicationID = O.ApplicationID AND O2.CustomerCode = O.CustomerCode AND O.Revenue = -O2.Revenue
WHERE O2.ApplicationID IS NULL
And this is the output:
CustomerCode ApplicationID InvoiceNumber Date Revenue
A 11 AA 2020-01-01 250
A 11 AA 2020-01-01 300
A 22 CC 2020-01-07 500
A 22 DD 2020-01-07 700
A 11 AA 2020-01-02 800

Returning NULLs for data which does not exist for certain dates [duplicate]

This question already has answers here:
Fill Missing Dates In a Date-Sequenced in SQL using Tally Table
(2 answers)
Closed 1 year ago.
I have a data table similar to the below:
EntityId
Date
Value
1
5/30/2021
42
1
6/30/2021
35
1
7/31/2021
59
1
8/31/2021
61
2
7/31/2021
98
2
8/31/2021
100
3
8/31/2021
34
I want to return all values in the "Value" column between calendar month end dates 5/31/2021 and 8/31/2021 for each unique entityId. However, each unique entityId does not always have a row for all such dates, in which I would like to return a null in the Value column should the date not exist. Using the query on the table should result in the following data:
EntityId
Date
Value
1
5/31/2021
42
1
6/30/2021
35
1
7/31/2021
59
1
8/31/2021
59
2
5/31/2021
NULL
2
6/30/2021
NULL
2
7/31/2021
98
2
8/31/2021
100
3
5/31/2021
NULL
3
6/30/2021
NULL
3
7/31/2021
NULL
3
8/31/2021
34
What would be the easiest way to accomplish this?
You need to start with a list of dates and entities. Let me assume they are all in the table. So, the idea is to use cross join to generate the rows in the result set. Then use left join to bring in the remaining data:
select e.entityid, d.date, t.value
from (select distinct entityid from data) e cross join
(select distinct date from data) d left join
data t
on t.entityid = e.entityid and t.date = d.date;
Note: This uses subqueries to generate the dates and entities. If this information is in other tables, you can directly use those tables.
To create the full range of EntityId's and Date's the query uses the CROSS JOIN of distinct values as 'range_cte'. The original data 'data_cte' is then FULL OUTER JOIN'ed with 'range_cte'. Something like this
declare
#start_dt date='20210531',
#end_dt date='20210831';
;with
data_cte(EntityId, [Date], [Value]) as (
select *
from (values (1, cast('20210531' as date), 42),
(1, cast('20210630' as date), 35),
(1, cast('20210731' as date), 59),
(1, cast('20210831' as date), 59),
(2, cast('20210731' as date), 98),
(2, cast('20210831' as date), 100),
(3, cast('20210831' as date), 34))
v(EntityId, [Date], [Value])),
unq_id_cte(EntityId) as (
select distinct EntityId
from data_cte),
nums_cte(n) as (
select *
from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) v(n)),
range_cte as (
select top(datediff(month, #start_dt, #end_dt)+1)
eomonth(#start_dt, row_number() over (order by (select null))-1) mo_dt
from nums_cte n1
cross join nums_cte n2)
select ui.EntityId, coalesce(r.mo_dt, d.[Date]) [Date], d.[Value]
from
unq_id_cte ui
cross join range_cte r
full join data_cte d on ui.EntityId=d.EntityId
and r.mo_dt=d.[Date]
order by ui.EntityId, r.mo_dt;
EntityId Date Value
1 2021-05-31 42
1 2021-06-30 35
1 2021-07-31 59
1 2021-08-31 59
2 2021-05-31 NULL
2 2021-06-30 NULL
2 2021-07-31 98
2 2021-08-31 100
3 2021-05-31 NULL
3 2021-06-30 NULL
3 2021-07-31 NULL
3 2021-08-31 34

Multiple joins on same table giving duplicate data

Here is the sale_order table which contains CCNID
sale_orderid client_channel_nameid
1 1
2 1
3 2
4 2
5 2
6 2
7 1
8 1
sale_order_item Table has sale_orderid as a foreign key
sale_order_itemid sale_orderid order_date selling_price
42219 1 2018-03-21 00:00:00 200
28948 2 2018-03-21 16:17:55 100
42220 3 2018-03-21 00:00:00 300
13194 4 2018-03-21 13:33:58 400
42839 5 2018-03-20 07:54:29 550
42840 6 2018-03-20 07:58:20 600
42086 7 2018-03-20 00:00:00 700
11691 8 2018-03-20 05:32:31 500
And I want to get the sum of price of soid of 21 and 20 dates in different columns grouped by CCNID
client_channel_nameid 21 20
1 300 1200
2 700 1150
I am joining Sale order twice which is giving me wrong results
select ccn.client_channel_nameid,
round (sum(soi.selling_price)),
round(sum(soi1.selling_price))
from app.client_channel_name ccn
join app.sale_order so on so.client_channel_nameid = ccn.client_channel_nameid
inner join app.sale_order_item soi on soi.sale_orderid = so.sale_orderid
join app.sale_order so1 on so1.client_channel_nameid = ccn.client_channel_nameid
inner join app.sale_order_item soi1 on soi1.sale_orderid = so1.sale_orderid
where ccn.clientid = 1
and to_char(soi.order_date, 'DD-MM-YYYY') = '20-03-2018'
and to_char(soi1.order_date, 'DD-MM-YYYY') = '21-03-2018'
group by client_channel_nameid;
You can group the data by CCNID and then only sum selling_price when the order_date day is the 21 or 20.
SELECT client_channel_nameid
, SUM(CASE
WHEN EXTRACT(day FROM order_date) = 21 THEN selling_price
ELSE 0
END) AS "21"
, SUM(CASE
WHEN EXTRACT(day FROM order_date) = 20 THEN selling_price
ELSE 0
END) AS "20"
FROM sale_order so
JOIN sale_order_item soi
ON soi.sale_orderid = so.sale_orderid
GROUP BY so.client_channel_nameid
Below query produce the desired result. From your above tried solution and question you asked I think you are looking for exactly 21 and 20 dates. The below will need slight changes with extra filters for add more dates ex.22,23,24...
with sale_order(
sale_orderid, client_channel_nameid
) as (
select
*
from
(
values
(1, 1),
(2, 1),
(3, 2),
(4, 2),
(5, 2),
(6, 2),
(7, 1),
(8, 1)
) as x(
sale_orderid, client_channel_nameid
)
),
sale_order_item(
sale_order_itemid, sale_orderid,
order_date, selling_price
) as (
select
*
from
(
values
(
42219, 1, '2018-03-21 00:00:00' :: timestamp,
200
),
(
28948, 2, '2018-03-21 16:17:55' :: timestamp,
100
),
(
42220, 3, '2018-03-21 00:00:00' :: timestamp,
300
),
(
13194, 4, '2018-03-21 13:33:58' :: timestamp,
400
),
(
42839, 5, '2018-03-20 07:54:29' :: timestamp,
550
),
(
42840, 6, '2018-03-20 07:58:20' :: timestamp,
600
),
(
42086, 7, '2018-03-20 00:00:00' :: timestamp,
700
),
(
11691, 8, '2018-03-20 05:32:31' :: timestamp,
500
)
) as x(
sale_order_itemid, sale_orderid,
order_date, selling_price
)
)
select
client_channel_nameid,
sum(selling_price) filter (where to_char(order_date, 'dd-mm-yy') = '21-03-2018') as date_21,
sum(selling_price) filter (where to_char(order_date, 'dd-mm-yy') = '20-03-2018') as date_20
from
sale_order so
join sale_order_item soi on soi.sale_orderid = so.sale_orderid
group by
so.client_channel_nameid

Fill date gaps in SQL query with zeros

I have the following structure (which came from some joins across tables,etc) which will be used to produce a chart.
Some IDs do not have data for all the dates, which result in some dashed lines in the chart.
The question is, how can I add the missing dates for each ID and fill their data cell with zeros?
As script:
(67, '2016-09-28 00:00:00.000',1),
(178, '2016-09-28 00:00:00.000',6),
(42, '2016-09-25 00:00:00.000',1),
(66, '2016-09-25 00:00:00.000',122),
(67, '2016-09-25 00:00:00.000',2),
(10, '2016-09-24 00:00:00.000',5),
(13, '2016-09-24 00:00:00.000',4),
(66, '2016-09-24 00:00:00.000',198),
(67, '2016-09-24 00:00:00.000',15),
(178, '2016-09-24 00:00:00.000',4),
(10, '2016-09-23 00:00:00.000',1),
(13, '2016-09-23 00:00:00.000',2),
(42, '2016-09-23 00:00:00.000',4),
(66, '2016-09-23 00:00:00.000',208),
(67, '2016-09-23 00:00:00.000',15)
Here is one method:
with t as (
<your query here>
)
select i.id, rt.roundedtime, coalesce(data, 0) as data
from (select distinct id from t) i cross join
(select distinct roundedtime rt from t) rt left join
t
on t.id = i.id and t.roundedtime = rt.roundedtime;
In other words, create the list of dates and ids using a cross join. Then use a left join to bring in your data.
This query uses select distinct on your original data to get the lists of dates and ids. There may be more efficient ways to get each of these lists.
One more way with calendar table and CROSS JOIN:
;WITH YourQueryOutput AS (
--put your select statement here
), calendar AS (
SELECT CAST(MIN(RoundedTime) as datetime) as d,
MAX(RoundedTime) as e
FROM YourQueryOutput
UNION ALL
SELECT DATEADD(day,1,d),
e
FROM calendar
WHERE d < e
)
SELECT t.ID,
c.d,
COALESCE(t1.[data],0) as [data]
FROM calendar c
CROSS JOIN (
SELECT DISTINCT ID
FROM YourQueryOutput
) t
LEFT JOIN YourQueryOutput t1
ON t.ID = t1.ID and t1.RoundedTime = c.d
ORDER BY t.ID, c.d
OPTION(MAXRECURSION 0)
Output for sample you provided
ID d data
10 2016-09-23 00:00:00.000 1
10 2016-09-24 00:00:00.000 5
10 2016-09-25 00:00:00.000 0
10 2016-09-26 00:00:00.000 0
10 2016-09-27 00:00:00.000 0
10 2016-09-28 00:00:00.000 0
...
178 2016-09-23 00:00:00.000 0
178 2016-09-24 00:00:00.000 4
178 2016-09-25 00:00:00.000 0
178 2016-09-26 00:00:00.000 0
178 2016-09-27 00:00:00.000 0
178 2016-09-28 00:00:00.000 6
You can check ISNULL(YourDataColumn,0) in SELECT statement where you used join.
Example:
SELECT
Q.QuestionId,
Q.SenderType,
ISNULL(Q.Data,0) AS Data
FROM #tblQuestion Q
LEFT JOIN #tblTeacher T ON Q.SenderId=T.Id AND Q.SENDERTYPE='TEACHER'
LEFT JOIN #tblInstitute I ON Q.SenderId=I.Id AND Q.SENDERTYPE='INSTITUTE'
IN above Select Statement Data column return 0 if no data available after join

How to write this query in SQL with category and date sum

I have a table in MySQL with these data belows:
DATE Category AMOUNT
2016-1-1 A 12
2016-1-1 B 10
2016-1-2 A 5
2016-1-3 C 1
2016-2-1 A 5
2016-2-1 B 6
2016-2-2 A 7
2016-2-3 C 3
How can I get the result as below:
MONTH TOTAL Category-A Category-B Category-C
2016 Jan 28 17 10 1
2016 Feb 21 12 6 3
If you're using MySQL this would work:
SELECT
DATE_FORMAT(DATE, '%Y %b') AS MONTH,
SUM(AMOUNT) AS TOTAL,
SUM(IF(CATEGORY='A', AMOUNT, 0)) AS `Category-A`,
SUM(IF(CATEGORY='B', AMOUNT, 0)) AS `Category-B`,
SUM(IF(CATEGORY='C', AMOUNT, 0)) AS `Category-C`
FROM your_table
GROUP BY MONTH;
For other database engines you might have to change that a little.
DECLARE #Table1 TABLE
(DATE varchar(8), CAT varchar(1), AMOUNT int)
;
INSERT INTO #Table1
(DATE, CAT, AMOUNT)
VALUES
('2016-1-1', 'A', 12),
('2016-1-1', 'B', 10),
('2016-1-2', 'A', 5),
('2016-1-3', 'C', 1),
('2016-2-1', 'A', 5),
('2016-2-1', 'B', 6),
('2016-2-2', 'A', 7),
('2016-2-3', 'C', 3)
;
Select Months,
[A][Category-A],
[B][Category-B],
[C][Category-C],
SUM([A]+[B]+[C])TOTAL
from (
select CAST(year(DATE) AS VARCHAR)+' '+CONVERT(CHAR(3), DATENAME(MONTH, date))Months,
CAT,
AMOUNT
from #Table1)T
PIVOT (SUM(AMOUNT) FOR cat IN ([A],[B],[C]))P
GROUP BY Months,[A],[B],[C]
ORDER BY CONVERT(CHAR(3), DATENAME(MONTH, Months)) desc