Find difference between two sets of records - sql

I have a set of data on sql server something like:
ID ID_Invoice Article Quantity Status
1 10 carrot 10 null
2 10 carrot 5 C
3 10 onion 8 null
4 10 onion 4 C
5 11 tomato 20 null
6 11 tomato 18 C
7 11 onion 2 null
8 11 onion 1 C
It means that a customer ordered 10 carrots and 8 onions (on one invoice) but actually received only 5 carrots and 4 onions. If status is null then it is original quantity, if status is C then it is corrected quantity
I need to generate a table like
ID ID_Invoice Article Quantity
1 10 carrot -5
2 10 onion -4
3 11 tomato -2
4 11 onion -1
which shows the difference between ordered quantity and real quantity on each invoice. I have no idea how to begin. Any help deeply appreciated :)

Option with simple CASE expression without excessive JOIN
SELECT ID_Invoice, Article,
SUM(CASE WHEN Status IS NULL
THEN -1 * Quantity ELSE Quantity END) AS Quantity
FROM dbo.test38
GROUP BY ID_Invoice, Article
Result:
ID_Invoice Article Quantity
10 carrot -5
10 onion -4
11 onion -1
11 tomato -2
Demo on SQLFiddle

Least resource intensive:
SELECT id_invoice
, article
, org_quantity
, new_quantity
, new_quantity - org_quantity diff
FROM (SELECT id_invoice
, article
, max(CASE WHEN status IS NULL THEN quantity else null END) org_quantity
, max(CASE WHEN status = 'C' THEN quantity else null END) new_quantity
FROM orders
GROUP BY id_invoice
, article)
See it working here: http://sqlfiddle.com/#!4/f96adf/14

You didn't specify, which RDBMS you're using, but my answer is ANSI-SQL standard compliant :) Works with every valid RDBMS out there.
SELECT
yt1.ID_Invoice,
yt1.Article,
yt2.Quantity - yt1.Quantity AS Quantity
FROM
yourTable yt1
INNER JOIN yourTable yt2 ON yt1.ID_Invoice = yt2.ID_Invoice
AND yt1.Article = yt2.Article
AND yt2.Status = 'C'
WHERE
yt1.Status IS NULL
This answer is asuming, there's always a record with Status NULL and corresponding row with status 'C'. If this is not the case, you'd have to adjust it like this:
SELECT
yt1.ID_Invoice,
yt1.Article,
CASE WHEN yt2.Quantity IS NULL THEN yt1.Quantity ELSE yt2.Quantity - yt1.Quantity END AS Quantity
FROM
yourTable yt1
LEFT JOIN yourTable yt2 ON yt1.ID_Invoice = yt2.ID_Invoice
AND yt1.Article = yt2.Article
AND yt2.Status = 'C'
WHERE
yt1.Status IS NULL

So first of all you have to separate the actual from the ordered by making 2 queries and then you have to left join the orders to the actual .. something like this
select
Recived.ID,
Recived.ID_Invoice,
Recived.Article,
Recived.Quantity - Ordered.Quantity as Quantity
from
(select * from dataTable where Status is null) as Ordered
left join (select * from dataTable where Status = 'C') as Recived on (Ordered.ID_Invoice = Recived.ID_Invoice and Ordered.Article = Recived.Article )
NOTE! you will be better if you have an id for each article to use in the "left join" instead of comparing varchars.
Here is a fiddle example: http://sqlfiddle.com/#!2/16666/1

Related

SQL Group by Sales Rep - Select 2 counts

I would like to query a table that has leads assigned by sales rep to return the unique number of leads grouped by agent and also the number sold. There can be multiple leads from one buyer, I would like to select distinct so each buyer is counted only once. Here is the layout of the data:
AgentId
BuyerEmail
Product
Category
1
lisa#gmail.com
Jeans
1
1
lisa#gmail.com
Hat
1
1
ryan#gmail.com
Shoes
3
2
mark#gmail.com
Jeans
1
2
mark#gmail.com
Socks
1
2
mark#gmail.com
Hat
1
4
john#gmail.com
Shirt
3
5
lou#gmail.com
Hat
3
5
tim#gmail.com
Shirt
3
I would like to return a dataset like the following:
AgentId
UniqueLeads
QtySold
1
2
1
2
1
0
4
1
1
5
2
2
I can query this individually but I can't get it to return in one result set. Here are the 2 separate queries:
SELECT COUNT(DISTINCT BuyerEmail) FROM SalesLeads GROUP BY InitialAgent
SELECT COUNT(DISTINCT BuyerEmail) FROM SalesLeads WHERE Category = 3 GROUP BY InitialAgent
How can I query the table and have both data points return in one result set? Please note, a category = 3 means it is sold.
You can use conditional aggregation to calculate QtySold in the same statement:
select AgentId,
count(distinct BuyerEmail) as UniqueLeads,
count(case when Category = 3 then Category end) as QtySold
from SalesLeads
group by AgentId
When Category is anything other than 3 the case statement returns null so that record isn't counted in the QtySold calculation.
db<>fiddle

Finding duplicate **Set of Values** in multiple rows in SQL Server table

I have a SQL Server database table with this sample data:
ProductID GenericID MG
---------------------------------
1 1 2g
1 2 5g
2 2 5g
3 1 2g
3 2 5g
4 1 2g
5 1 2g
5 3 7g
6 2 5g
7 1 2g
8 1 2g
I want to find out the query to select data
if I select ProductID=1 then the query should check what GenericID are associated with ProductID=1
In above data case if user select ProductID=1 then query will check GenericID=1 and 2 are associated with ProductID=1.
Then after I want to go through all rows and select those rows who has the same Unique ProductID and also having only GenericID=1 and 2.
as in above case the final output will be as shown below....
I select ProductID=1 and output has four rows, because only ProductId 3 has same GenericID as were of ProductId=1.
If I select only ProductId=1 then I want to get all the rows with the same exact set of GenericID values as ProductID=1, which is the set { 1, 2 } in my sample data. I am struggling with the query logic.
For example - I select ProductID=1, this is the output that I want is as follows, because ProductID 3 has the same set of GenericID values as ProductID 1.
ProductID GenericID MG
-------------------------------
1 1 2g
1 2 5g
3 1 2g
3 2 5g
GenericID can be on or multiple dynamic values.
Another example - if I select ProductID=7, this is the output I want:
In this example - It will only get those results that are having only GenericID=1 because ProductID=7 has only GenericID=1. any set of productID which is having GenericID=1 and also that set includes other GenericID will be neglected.
ProductID GenericID MG
------------------------------
7 1 2g
8 1 2g
4 1 2g
I need to find out the query to select the required output.
I want all of the products that have the same set of generic id's as the predicate product.
The simplest method is probably to use string_agg():
with t as (
select productID, string_agg(genericId, ',') within group (order by genericId) as genericIds
from sample
group by productID
)
select s.*
from t join
t t2
on t.genericIds = t2.genericIds and t2.productId = 1 join
sample s
on s.productId = t.productId;
Gordon, thanks a lot for your prompt response, basically I forget to inform you that I am using SQL 2014 and that's why string_agg(): action function wasn't helpful for me but I really appreciate your help and the prompt response that make my day. Here I created my query with the help of your other query and you became and very helpful resource for me.
select PG.PID2 as Alternatives
from (select d1.ProductID as PID1, d2.ProductID as PID2
from (select distinct ProductID from ProductsGenerics Where ProductID=#PID) d1 cross join
(select distinct ProductID from ProductsGenerics) d2
) PG left outer join
ProductsGenerics e1
on e1.ProductID = PG.PID1 full outer join
ProductsGenerics e2
on PG.PID2 = e2.ProductID and e1.genericid = e2.GenericID-- and e1.MG = e2.MG
group by PG.PID1, PG.PID2
having SUM(case when e1.GenericID is null then 1 else 0 end) = 0 and
SUM(case when e2.GenericID is null then 1 else 0 end) = 0

Fetching data from DB and populate a partitioned List

I am confused about this both from front end point of view as well as querying the data from SQLite Database. If you have any idea how to solve either of these please do answer.
SQLite Database
I have a table likes this:
transactionId | productId | quantity
1 2 1
2 4 0
3 1 null
4 3 1
5 9 1
6 6 0
7 1 1
8 7 1
9 8 1
10 2 1
11 0 null
12 3 1
13 5 1
14 7 1
15 1 0
16 2 1
17 9 1
18 0 null
19 2 1
Now I want to display this data in groups of 5 units(i.e. groups till 5 units are completed) in list in my flutter app.
So 1st group will have 8 items,
2nd will have 6 items,
and 3rd group will have 5 items
(and is still incomplete since more items can be added till quantity for that group becomes 5)
Something like this:
Now my App can have multiple groups like this. Also, I don't think Grid view builder can work here since for each group I'll have to display some data for the group as well as accumulated data (which isn't shown in the picture)
Questions:
1) How to query data from SQFLite database?
2) How to display the queried data in my Flutter App front end?
Unfortunately, this type of problem requires a recursive CTE (or other iterative processing).
Assuming that transactionId is consecutive with no gaps:
with recursive cte as (
select transactionId, productId,
coalesce(quantity, 0) as quantity,
1 as bin
from t
where transactionId = 1
union all
select t.transactionId, t.productId,
(case when cte.quantity > 5
then 0 else cte.quantity
end) + coalesce(t.quantity, 0) as quantity,
(case when cte.quantity > 5 then 1 else 0 end) + cte.bin as bin
from cte join
t
on t.transactionId = cte.transactionId + 1
)
select *
from cte;
If transactionId has gaps or other issues, just use row_number() (in another CTE) to create an appropriate column for the where clauses.

Group by with set of values in PostgreSQL

I have a product table:
productid product
1 A-110
2 B-110
3 C-400
4 D-401
And orderditems table:
orderitemid productid qty
1 1 10
2 2 10
3 3 10
4 3 10
5 4 10
I can group by based on product as:
select productid, sum(qty)
from ordereditems
group by productid
Which gives:
1 10
2 10
3 20
4 10
However for this query productid 1 & 2 , 3 & 4 are the same.
Meaning that I want to see:
1 + 2 20
3 + 4 30
Basically I want the query to understand that 1 & 2 are the same group and 3 & 4 are the same group.
How can i do that?
Edit:
Products 1 & 2 , 3 & 4 are not the same. However they are of the same family... What I want is to see how much we sell per family and not per product.
This is why the desired out put is:
1 + 2 20
3 + 4 30
A hacky version if you know that 1&2 are the same and 3&4 are the same, and those are the only things the same.
select
case
when productid in (1,2) then '1 + 2'
when productid in (3,4) then '3 + 4'
else productid
end as product_ids, sum(qty)
from ordereditems
group by
case
when productid in (1,2) then '1 + 2'
when productid in (3,4) then '3 + 4'
else productid
end
A better approach is to record in the database that two products are the same. Either by linking the duplicated products (2,4) to the product they're the same as (1,3) or by creating a new table family etc. and recording that products 1,2 are related to the same family.
family
familyid name
1 1+2
2 3+4
Extend product table
productid product familyid
1 A-110 1
2 B-110 1
3 C-400 2
4 D-401 2
then you can group on family
select f.name, sum(qty)
from ordereditems oi
inner join product p on oi.productid = p.productid
inner join family f on p.familyid = f.familyid
group by f.name
Pls add a GroupID/Category field to your table.
productid product GroupID
1 A-110 A
2 B-110 A
3 C-400 B
4 D-401 B
Then use the query
select GroupID, sum(qty) as Sold
from ordereditems
group by GroupID
This will represent as group sale as if you have more than 10 products in a group your productid will make complicity to understand.
If it works for you pls mark as helpful.. :)

complex paratition sum in postgresql

I have tables as follow:
A deliveries
delveryid clientid deliverydate
1 10 2015-01-01
2 10 2015-02-02
3 11 2015-04-08
B items in deliveris
itemid deliveryid qty status
70 1 5 1
70 1 8 2
70 2 10 1
72 1 12 1
70 3 100 1
I need to add a column to my query that gives me the qty of each part in other deliveris of the same client.
meaning that for given data of client 10 and delivery id 1 I need to show:
itemid qty status qtyOther
70 5 1 10 //itemid 70 exists in delivery 2
70 8 2 10 //itemid 70 exists in delivery 2
72 12 1 0 //itemid 72 doesn't exists in other delivery of client 11
Since I need to add qtyOther to my existing qry i'm trying to avoid using Group By as it's a huge query and if I use SUM in select I will have to group by all items in select.
This is what I have so far:
Select ....., coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) ,0) AS qtyOther
FROM B b
LEFT JOIN A a USING
LEFT JOIN (other tables)
WHERE clientid=10 ....
This query gives me the total sum of qty per itemid for specific clientid, regardless of which delivery it is. How do I change it so it will consider the delivryid? I need something like:
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) FROM B where deliveryid<>b.deliveryid ,0) AS qtyOther
Any suggestions how to do that?
Note: I can NOT change the condition in WHERE.
I think you just want to subtract out the total for the current delivery:
Select .....,
(coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid), 0) -
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid, a.deliveryid), 0)
) as qtyOther