SQL - Summing/counting rows based on matching columns - sql

I have the 2 following tables
Tracking
tracking_id item_extension quantity
a 144 100
b 144 200
c 250 150
Account
tracking_id account
a 999
b 999
c 999
Here's my query -
SELECT sum(qty) as qty, count(item_extension) as total, t.tracking_id, item_extension, account
FROM Tracking t
INNER JOIN Account a ON t.tracking_id = a.tracking_id
GROUP BY t.tracking_id, item_extension, account
What I want to happen here is get count of item_extension and sum of quantity based on matching account/item_extension fields. So because there are 2 rows with matching account and item_extension fields, it should sum up 2 of them like so:
qty total tracking_id item_extension account
300 2 a 144 999
300 2 b 144 999
150 1 c 250 999
Instead I get this result:
qty total tracking_id item_extension account
100 1 a 144 999
200 1 b 144 999
150 1 c 250 999
Is there a good way of doing this?

You want to count item_extension values that are not in the current row. So, use window functions. I think this does what you want:
SELECT sum(qty) as qty,
sum(count(*)) over (partition by item_extension) as total,
t.tracking_id, item_extension, account
FROM Tracking t
INNER JOIN Account a ON t.tracking_id = a.tracking_id
GROUP BY t.tracking_id, item_extension, account;

Related

Sql code returns unexpected results. How to correct it

I'm using Firebird 3. There are 'sellers' master table for my contragent sellers, detailed 'Doc' table for goods income documents and subdetail table 'paym' for payments.
Seller: Seller_id, seller
Docs: doc_id, doc_summa, part_id
Paym: paid_id, doc_id, paid
Seller:
seller_id
seller
8
Firm1
45
Firm2
Docs:
doc_id
doc_summa
seller_id
346
1000
8
347
600
45
348
800
45
Paym:
paym_id
doc_id
paid
1
346
100
2
346
100
3
347
200
4
348
100
5
348
50
My aim is to get my residual debts(income-payment) like this:
seller_id
summa
paid
debt
8
1000
200
800
45
1400
350
1050
but get this multiplied, wrong summas:
seller_id
summa
paid
debt
8
2000
200
800
45
2200
350
1850
SELECT
s.seller_id,
sum(d.doc_summa) as summa,
sum(p.paid) as paid,
sum(d.doc_summa)-sum(p.paid) as debt
FROM seller s Left Join Docs d on s.seller_id=d.seller_id
Left Join paym p on p.doc_id= d.doc_id
GROUP BY s.seller_id
What is wrong in my SQL code?
The problem with your query is that because there are multiple payments for doc_ids 346 and 348, you will have two rows with a doc_summa of 1000 and 800 respectively, when you then sum them, the total becomes 2000 for seller_id 8 and 2200 for seller_id 45.
To solve this, you need to consolidate (sum) the payments before joining with docs. For example like the following:
with consolidated_paym as (
select
doc_id,
sum(paid) as total_paid
from paym
group by doc_id
)
select
s.seller_id,
sum(d.doc_summa) as summa,
sum(p.total_paid) as paid,
sum(d.doc_summa - p.total_paid) as debt
from seller s
left join docs d
on d.seller_id = s.seller_id
left join consolidated_paym as p
on p.doc_id = d.doc_id
group by s.seller_id
Fiddle: https://dbfiddle.uk/6_vyCu0w

returning mode != average items and returning min, max, mode , average

I have a table as so:
product_table
sku_id
product_name
desc
1
productA
desc 1
2
productB
desc 2
hub_table_pricing
sku_id
product_name
hub
pricing
1
productA
a
55000
1
product a
b
45000
1
product A
c
55000
1
product A
d
0
2
productb
a
55000
2
product B
b
45000
2
product ba
c
55000
2
product b
d
0
Output (group by sku_id, get min max mode avg and return product name from product table
sku_id
product_name
desc
min
max
mode
avg
1
productA
desc 1
45000
55000
55000
50000
2
productB
desc 2
45000
55000
55000
50000
While ignoring all 0 and returning only items where mode does not match average price.
If there are 2 modes, then return both of them in the same field(is this possible?)
How do I do this in sql bigquery?
If it's in python, I did a groupby (skuid)['price].mean() and so on, combine all the tables and did a if not match.
does this translate as this?
select
tbl.sku_id,
tbl.price,
from
(select
distinct
sku_id,
price
Avg(price) as avg
from hub_table_pricing
where price is not 0
group by price) as avg)
join (select
distinct
sku_id,
price
min(price) as min
from hub_table_pricing
where price is not 0
group by price) as min) on avg.sku_id = min.sku_id as tbl
but can't seem to do mulitple product modus? how do i achieve this?

SQL DB2 Toad - Sum from two tables by ID

I was hoping to find the sum from two tables with columns ID and Amount, grouping by ID.
My first attempt was to UNION the two tables first and then conduct a sum and group by, but I was hoping to know of a better way.
Inputs:
Table 1
ID Amount
123 100
123 100
145 500
167 600
Table 2
ID Amount
123 100
123 100
145 500
199 600
Output
ID Amount
123 400
145 1000
167 600
199 600
You can do:
select id, sum(amount) as amount
from (
select id, amount from table_1
union all
select id, amount from table_2
) x
group by id

Need to group records based on matching reversal in sql

I have a tricky scenario to aggregate the data.
Data in my source table is as follows.
CustomerId Transaction Type Transaction Amount
1 Payment 100
1 ReversePayment -100
1 payment 100
1 ReversePayment -100
1 Payment 100
1 Payment 100
Requirement is as follows:
If the payment as a assoociated Reversepayment with matched amount, sum these two records.
If the payment does not have an associated Reverse payment, consider it as orphan(dont sum it).
I want output to be like this.
CustomerId Transaction Type Transaction Amount
1 Payment,ReversePayment 0
1 payment,ReversePayment 0
1 payment 100
1 Payment 100
In this scenario,
First record which is payment has an associated reverse payment (2nd record), Hence the sum becomes 0
Third record which is payment has an associated reverse payment (4th record), then the sum becomes 0
Fifth and sixth does not have associated reversals. dont sum these records.
Second Example:
Data in the source as follows:
CustomerId Transaction Type Transaction Amount
1 Payment 100
1 ReversePayment -100
1 payment 300
1 ReversePayment -300
1 Payment 400
1 Payment 500
Expected Output
CustomerId Transaction Type Transaction Amount
1 Payment,ReversePayment 0
1 payment,ReversePayment 0
1 payment 400
1 Payment 500
Second example requirement:
-As first and second records (payment and its associated reverse payment got
matched) ,sum these two records, output is 0.
- As third and fourth records (payment and its associated reverse payment got
matched), sum these two records, output is 0.
- Fifth and sixth does not have associated reversals. don't sum these records.
I got solutions in group, but data is not always guaranteed to have orphan records as 'payments'. Some times they are 'Payments' and some times they are 'ReversePayments'. Can some help me get ouptut like the below (using rank or rownumber functions ) so that i can group by using RRR column.
CustomerId Transaction Type Transaction Amount RRR
1 Payment 100 1
1 ReversePayment -100 1
1 payment 100 2
1 ReversePayment -100 2
1 Payment 100 3
1 Payment 100 4
CustomerId Transaction Type Transaction Amount RRR
1 Payment 100 1
1 ReversePayment -100 1
1 payment 300 2
1 ReversePayment -300 2
1 Payment 400 3
1 Payment 500 4
You can enumerate the different types and then aggregate:
select customerid,
listagg(ttype, ',') within group (order by ttype) as types,
sum(amount) as amount
from (select t.*,
row_number() over (partition by customerid, ttype, amount order by customerid) as seqnum
from t
) t
group by customerid, seqnum;
Edited to include your second scenario:
Using rownum to enforce inherent ordering (i.e. transactions happened in the order you've listed ), since your example is missing a transaction id or transaction time
SQL> select * from trans_data2;
CUSTOMER_ID TRANSACTION_TY TRANSACTION_AMOUNT
----------- -------------- ------------------
1 Payment 100
1 ReversePayment -100
1 payment 300
1 ReversePayment -300
1 Payment 400
1 Payment 500
6 rows selected.
SQL> select customer_id,
2 case
3 when upper(next_transaction) = 'REVERSEPAYMENT' then transaction_type||','||next_transaction
4 else transaction_type
5 end transaction_type,
6 case
7 when upper(next_transaction) = 'REVERSEPAYMENT' then transaction_amount + next_transaction_amount
8 else transaction_amount
9 end transaction_amount
10 from (
11 select customer_id, transaction_type, transaction_amount,
12 lead (transaction_type) over ( partition by customer_id order by transaction_id ) next_transaction,
13 nvl(lead (transaction_amount) over ( partition by customer_id order by transaction_id),0) next_transaction_amount
14 from ( select rownum transaction_id, t.* from trans_data2 t )
15 ) where upper(transaction_type) = 'PAYMENT'
16 ;
CUSTOMER_ID TRANSACTION_TYPE TRANSACTION_AMOUNT
----------- ----------------------------- ------------------
1 Payment,ReversePayment 0
1 payment,ReversePayment 0
1 Payment 400
1 Payment 500

SQL - Case when product exists, fill up its corresponding value to all rows within the partition

I have the something like the following monthly data set.
I have a Product, company ID, Date, and Quantity. A company (denoted by Company ID) can buy multiple products. I want to create a new column that will have the quantity of Product 'C' if the company bought in the month at each line item. If Product 'C' is not bought, then return 0.
Product Company_ID Date Quantity Desired_Calculated_Column
A 1 5/1/2019 100 300
B 1 5/1/2019 200 300
C 1 5/1/2019 300 300
A 2 6/1/2019 150 125
B 2 6/1/2019 250 125
C 2 6/1/2019 125 125
A 3 7/1/2019 175 0
B 3 7/1/2019 275 0
I have been trying to partition the data based on Product and Company ID. I have been trying to leverage the LAST_VALUE but haven't been successful.
LAST_VALUE(quantity) OVER (PARTITION BY Date, Company_ID
ORDER BY product_group
) AS Desired_Calculated_Column
You don't want last_value(). You can use conditional aggregation, assuming that 'C' occurs once per group:
MAX(CASE WHEN product_group = 'C' THEN quantity ELSE 0 END) OVER
(PARTITION BY Date, Company_ID) AS C_quantity