How to query SQLite to find sum, latest record, grouped by id, in between dates? - sql

Items (itemId, itemName)
Logs (logId, itemId, qtyAdded, qtyRemoved, availableStock, transactionDate)
Sample Data for Items:
itemId itemName
1 item 1
2 item 2
Sample Data for Logs:
logid itemId qtyAdded qtyRemoved avlStock transDateTime
1 2 5405 0 5405 June 1 (4PM)
2 2 1000 0 6405 June 2 (5PM)
3 2 0 6000 405 June 3 (11PM)
I need to see all items from Items table and their SUM(qtyAdded), SUM(qtyRemoved), latest availableStock (there's an option for choosing the range of transactionDate but default gets all records). Order of date in final result does not matter.
Preferred result: (without date range)
itemName qtyAddedSum qtyRemovedSum avlStock
item 1 6405 6000 405
item 2 <nothing here yet>
With date Range between June 2 (8AM) and June 3 (11:01PM)
itemName qtyAddedSum qtyRemovedSum avlStock
item 1 1000 6000 405
item 2 <no transaction yet>
So as you can see, final result is grouped which makes almost all my previous query correct except my availableStock is always wrong. If I focus in the availableStock, I can't get the two sums.

you could use group by sum, and between
select itemName, sum(qtyAdded), sum(qtyRemoved), sum(avlStock)
from Items
left join Logs on logs.itemId = items.itemId
where transDateTime between '2017-06-02 08:00:00' and '2017-06-03 23:00:00'
group by itemId
or
If you need the last avlStock
select itemName, sum(qtyAdded), sum(qtyRemoved), tt.avlStock
from Items
left join Logs on logs.itemId = items.itemId
INNER JOIN (
select logid,avlStock
from logs
inner join (
select itemId, max(transDateTime) max_trans
from Logs
group by itemId
) t1 on logs.itemId = t1.ItemId and logs.transDateTime = t1.max_trans
) tt on tt.logId = Logs.itemId
where transDateTime between '2017-06-02 08:00:00' and '2017-06-03 23:00:00'
group by itemId

Okay, I tried both of these and they worked, can anyone confirm if these are already efficient or if there are some more efficient answers there.
SELECT * FROM Items LEFT JOIN
(
SELECT * FROM Logs LEFT JOIN
(
SELECT SUM(qtyAdd) AS QtyAdded, SUM(qtySub) AS QtyRemoved, availableStock AS Stock
FROM Logs WHERE transactionDate BETWEEN julianday('2017-07-18 21:10:40')
AND julianday('2017-07-18 21:12:00') GROUP BY itemId
)
ORDER BY transactionDate DESC
)
USING (itemId) GROUP BY itemName;
SELECT * FROM Items LEFT JOIN
(
SELECT * FROM Logs LEFT JOIN
(
SELECT SUM(qtyAdd) AS QtyAdded, SUM(qtySub) AS QtyRemoved, availableStock AS Stock
FROM Logs GROUP BY itemId
)
ORDER BY transactionDate DESC
)
USING (itemId) GROUP BY itemName;

Related

SQL Count column remaining at 1

I would like to display a running total of Invoice_Amount. Here is my current query:
SELECT cust_name, COUNT(*) as Invoice_Amount, Invoice.invoice_date
FROM Customer, Invoice
WHERE Customer.customer_id = Invoice.customer_id
GROUP BY Invoice.customer_id, Customer.cust_name,invoice_date;
and here is the current output:
cust_name Invoice_Amount invoice_date
Company A 1 2000-10-12 00:00:00.000
Company B 1 2000-09-22 00:00:00.000
Company C 1 2000-05-26 00:00:00.000
Company D 1 2000-08-15 00:00:00.000
Company E 1 2000-11-15 00:00:00.000
Company E 1 2000-05-02 00:00:00.000
Where I would like the Invoice_Amount in both cases to read 2 like so:
cust_name Invoice_Amount invoice_date
Company A 1 2000-10-12 00:00:00.000
Company B 1 2000-09-22 00:00:00.000
Company C 1 2000-05-26 00:00:00.000
Company D 1 2000-08-15 00:00:00.000
Company E 2 2000-11-15 00:00:00.000
Company E 2 2000-05-02 00:00:00.000
This is so I can eventually do something along the lines of:
HAVING (COUNT(*) > 1)
How would I go about getting to this result
There is no need for a GROUP BY or a HAVING because you're not actually grouping by anything in the final result.
;;;/* CTE with leading semi-colons for safety */;;;WITH src AS
(
SELECT c.cust_name, i.invoice_date,
COUNT(i.invoice_date) OVER (PARTITION BY i.customer_id)
AS Invoice_Count
FROM dbo.Customer AS c
INNER JOIN dbo.Invoice AS i
ON c.customer_id = i.customer_id
)
SELECT cust_name, Invoice_Count, invoice_date
FROM src
-- WHERE Invoice_Count > 1;
Well, as from your data, the combinations of invoice_date and cust_name seem to be unique - as COUNT(*) always returns 1.
You now seem to need the count value that you call invoice_amount to tally up for the same cust_name. 'Chopsticks' is occurring twice in your report, and, for 'Chopsticks', you need the value 2. But, still, you want to keep both rows.
Functions that sort-of aggregate data, but still return the same number of rows as the input, are not GROUP BY or aggregate functions, they are window functions, or OLAP/Analytic functions.
So, start from your grouping query, but then select from it, applying an OLAP function , and select from that outer query in turn, filtering for the OLAP function's result:
WITH
grp AS (
SELECT
cust_name
, count(*) AS invoice_amount
, invoice.invoice_date
FROM customer
JOIN invoice ON customer.customer_id = invoice.customer_id
GROUP BY
invoice.customer_id
, customer.cust_name
, invoice_date;
)
,
olap AS (
SELECT
cust_name
, SUM(invoice_amount) OVER(PARTITION BY cust_name) AS invoice_amount
, invoice_date
FROM grp
)
SELECT
*
FROM olap
WHERE invoice_amount > 1;

Join tables based on dates with check

I have two tables in PostgreSQL:
Demans_for_parts:
demandid partid demanddate quantity
40 125 01.01.17 10
41 125 05.01.17 30
42 123 20.06.17 10
Orders_for_parts:
orderid partid orderdate quantity
1 125 07.01.17 15
54 125 10.06.17 25
14 122 05.01.17 30
Basicly Demans_for_parts says what to buy and Orders_for_parts says what we bought. We can buy parts which do not list on Demans_for_parts.
I need a report which shows me all parts in Demans_for_parts and how many weeks past since the most recent matching row in Orders_for_parts. note quantity field is irrelevent here,
The expected result is (if more than one row per part show the oldes):
partid demanddate weeks_since_recent_order
125 01.01.17 2 (last order is on 10.06.17)
123 20.06.17 Unhandled
I think the tricky part is getting one row per table. But that is easy using distinct on. Then you need to calculate the months. You can use age() for this purpose:
select dp.partid, dp.date,
(extract(year from age(dp.date, op.date))*12 +
extract(month from age(dp.date, op.date))
) as months
from (select distinct on (dp.partid) dp.*
from demans_for_parts dp
order by dp.partid, dp.date desc
) dp left join
(select distinct on (op.partid) op.*
from Orders_for_parts op
order by op.partid, op.date desc
) op
on dp.partid = op.partid;
smth like?
with o as (
select distinct partid, max(orderdate) over (partition by partid)
from Orders_for_parts
)
, p as (
select distinct partid, min(demanddate) over (partition by partid)
from Demans_for_parts
)
select p.partid, min as demanddate, date_part('day',o.max - p.min)/7
from p
left outer join o on (p.partid = o.partid)
;

sql Left join giving duplicate multiple values

Friends I am unable to fetch correct result Please suggest , Thanks in Advance .
I have two tables and trying to get balance quantity
One is Purchase Table (purchase_detail)
Pur_Date Item_Id Pur_Qty
2014-10-08 12792 25
2014-11-01 133263 20
2014-10-01 133263 2
2014-11-20 12792 10
Second is Sale Table (sale_detail)
Sale_Date Item_Id Sale_Qty
2014-11-17 133263 -6
2014-11-05 12792 -1
2014-11-24 133263 -2
2014-10-28 12792 -6
2014-11-05 133263 -2
After using left join
SQL :
select a.pur_item, sum(a.pur_qty + b.sold_qty ) as bal_qty
from purchase_item_qty_amount a left join sale_item_qty_amount b
on a.pur_item =b.sale_item where a.pur_item IN( 12792,133263)
group by 1;
Result - But it's incorrect
Item_Id Bal_qty
12792 56
133263 46
Result - It should be
Item_Id Bal_qty
12792 28
133263 12
Try this untested query:
select pur_item, sum(as bal_qty) from (
select pur_item, sum(a.pur_qty) as bal_qty
from purchase_item_qty_amount a
group by 1
union
select pur_item, sum(b.sold_qty)as bal_qty
from sale_item_qty_amount b
group by 1)
group by 1
You have to use union instead of join as the left rows have multiple matches to the right which cause too much data to be calculated. Try running your query without the sum and group by to see the raw data being calculated and you will see what I mean.
Your query with no group/sum:
select a.pur_item, a.pur_qty, b.sold_qty from purchase_item_qty_amount a left join sale_item_qty_amount b on a.pur_item = b.sale_item where a.pur_item IN (12792,133263);
Here is a query that works as you would expect:
select item, sum(a.pur_qty) + sum(a.sold_qty) as bal_qty from (select pur_item as item, sum(pur_qty) as pur_qty, 0 as sold_qty from purchase_item_qty_amount where pur_item in (12792,133263) group by pur_item union select sale_item as item, 0 as pur_qty, sum(sold_qty) as sold_qty from sale_item_qty_amount where sale_item in (12792,133263) group by sale_item) a group by item;
Here is the full dummy of your question,try this one you will get your desired result:-
purchase table
create table pt(Pur_Date date,Item_Id number, Pur_Qty number)
insert into pt values('08.oct.2014',12792,25);
insert into pt values('01.nov.2014',133263,20);
insert into pt values('01.oct.2014',133263,2);
insert into pt values('20.nov.2014',12792,10);
sales table
create table st(Sale_Date date , Item_Id number, Sale_Qty number)
insert into st values('17.nov.2014',133263,-6);
insert into st values('05.nov.2014',12792,-1);
insert into st values('24.nov.2014',133263,-2);
insert into st values('28.oct.2014',12792,-6);
insert into st values('05.nov.2014',133263,-2);
select purchase.Item_Id,(purchase.pur_qty+sale.sale_qty) bal_qty
from
(
select a.Item_Id, sum(a.pur_qty) pur_qty
from pt a
group by a.Item_Id
) purchase
left join
(
select b.Item_Id,
sum(b.sale_qty ) sale_qty
from st b
group by b.Item_Id
) sale
on sale.Item_Id = purchase.Item_Id
where purchase.Item_Id in (12792,133263)
order by 1;

SQL Full Join with Where

I am trying to join two tables in SQL, one containing a list of items, and the other with the dates they were sold. I got the join part down, but I need to get the information from the REPORT table, but all items in the VENDORS table. I need the items that were not sold to show up as NULL, or preferably 0. This is the code I'm using so far, and it's only showing the items that were sold on the given day.
SELECT t2.[DATE]
,t1.[VENDOR]
,t1.[UPC]
,t2.[QTY]
,t2.[AMOUNT]
FROM [STORESQL].[dbo].[VENDORS] t1
LEFT OUTER JOIN [STORESQL].[dbo].[REPORT] t2 ON t1.UPC=t2.UPC
WHERE VENDOR='119828' AND DATE='2011-11-8'
and example of the tables are
VENDORS:
VENDOR UPC
119828 1
119828 2
119828 3
REPORT:
DATE UPC QTY AMOUNT
2011-11-8 1 1 9.99
2011-11-8 3 2 18.98
The current code is resulting in
DATE VENDOR UPC QTY AMOUNT
2011-11-8 119828 1 1 9.99
2011-11-8 119828 3 2 18.98
I need it to show
DATE VENDOR UPC QTY AMOUNT
2011-11-8 119828 1 1 9.99
2011-11-8 119828 2 0 0.00
2011-11-8 119828 3 2 18.98
I know i'm doing something wrong, but I don't know what that is.
Thanks in advance.
Try this instead:
SELECT t2.[DATE]
,t1.[VENDOR]
,t1.[UPC]
,t2.[QTY]
,t2.[AMOUNT]
FROM [STORESQL].[dbo].[VENDORS] t1
LEFT OUTER JOIN [STORESQL].[dbo].[REPORT] t2 ON t1.UPC=t2.UPC AND DATE='2011-11-8'
WHERE VENDOR='119828'
The where clause against DATE makes your outer join to function as an inner join because the comparison will be against a null value in DATE. Moving the comparison to the ON clause should deal with that.
To get a 0 instead of null as a result you can use COALESCE:
,COALESCE(t2.[QTY], 0)
,COALESCE(t2.[AMOUNT], 0)
Your date column will also be null and that can be fixed with COALESCE as well:
COALESCE(t2.[DATE], '2011-11-8')
This is incompatible:
WHERE t2.DATE='2011-11-8'
I need the items that were not sold to show up as NULL.
The first WHERE clause will cancel all effects of an OUTER JOIN, because you remove all those records where there was no match for your row in T1. Maybe you want:
WHERE VENDOR='119828' AND (DATE='2011-11-8' OR DATE IS NULL)
WITH VENDORS
AS
(
SELECT *
FROM (
VALUES (119828, 1),
(119828, 2),
(119828, 3)
) AS T (VENDOR, UPC)
),
REPORT
AS
(
SELECT *
FROM (
VALUES ('2011-11-08T00:00:00', 1, 1, 9.99),
('2011-11-08T00:00:00', 3, 2, 18.98)
) AS T ("DATE", UPC, QTY, AMOUNT)
),
AllReportDates
AS
(
SELECT DISTINCT "DATE"
FROM REPORT
)
SELECT r."DATE", v.VENDOR, v.UPC, r.QTY, r.AMOUNT
FROM VENDORS v
JOIN REPORT r
ON v.UPC = r.UPC
UNION
SELECT a."DATE", v.VENDOR, v.UPC, 0 AS QTY, 0.00 AS AMOUNT
FROM VENDORS v
CROSS JOIN AllReportDates a
WHERE NOT EXISTS (
SELECT *
FROM REPORT r
WHERE r.UPC = v.UPC
AND r."DATE" = a."DATE"
);

Oracle get rows that exactly matches the list of values

I have a details table and I want to get the record that exactly matches the list of values in another table.
Here is a scenario:
OrderDetailTable
OrderID ItemID
1 1
1 2
1 3
1 4
2 1
2 2
2 4
3 1
3 2
3 3
4 1
4 2
OrderedTable
ItemID
1
2
Now I want to get the OrderID that has the exact ItemID matches with OrderedTable ItemID. In the above scenario OrderID 1 is valid since ItemID 1,2,3 is exactly matched with OrderedTable ItemID.
I used the join but it did not work. It gave me both OrderID 1,2 . How do I do it any ideas??
Try this:
SELECT OrderID
FROM OrderDetailTable JOIN OrderedTable USING (ItemID)
GROUP BY OrderID
HAVING COUNT(DISTINCT ItemID) = (SELECT COUNT(DISTINCT ItemID) FROM OrderedTable)
The idea, in a nutshell, is as follows:
Count how many OrderDetailTable rows match OrderedTable by ItemID,
and then compare that to the total number of ItemIDs from OrderedTable.
If these two numbers are equal, the given OrderID "contains" all ItemIDs. If the one is smaller than the other, there is at least one ItemID not contained in the given OrderID.
Depending on your primary keys, the DISTINCT may not be necessary (though it doesn't hurt).
try
SELECT * FROM OrderDetailTable WHERE OrderID NOT IN
(
SELECT A.OrderID FROM
(
SELECT
Y.OrderID
, OT.ItemID
, (SELECT Z.ItemID
FROM OrderDetailTable Z
WHERE Z.ItemID = OT.ItemID AND Z.OrderID = Y.OrderID
) I
FROM OrderDetailTable Y, OrderedTable OT
) A
WHERE A.I IS NULL);
EDIT - as per request the better syntax:
SELECT * FROM
OrderDetailTable Z WHERE Z.ORDERID NOT IN
(
SELECT O1 FROM
(SELECT Y.ORDERID O1, YY.ORDERID O2 FROM
OrderDetailTable Y CROSS JOIN OrderedTable OT
LEFT OUTER JOIN OrderDetailTable YY ON
YY.ORDERID = Y.ORDERID AND YY.ITEMID = OT.ITEMID ) ZZ WHERE ZZ.O2 IS NULL);