So I have 2 tables.
the first one would be the bill:
id
total
client_code
created_at
1
10
1
2022-02-01
2
20
1
2022-02-01
3
20
3
2022-03-01
the second would be the product (for a bill):
bill_id
category
total
1
Electronic
2
1
Food
5
1
Food
3
2
Food
10
2
Food
10
3
Food
10
3
Food
10
What I want to get with my query is the average spending by a client for each month over the last 4 months for example.
my query right now is looking like this for the first 2 months and it works, I get the result I want:
SELECT
COALESCE(AVG(first.total),0),
COALESCE(AVG(second.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as first),
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as second)
but as soon as I get to 3 months it doesn't anymore:
SELECT
COALESCE(AVG(first.total),0),
COALESCE(AVG(second.total),0),
COALESCE(AVG(third.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as first),
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as second),
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-03-01' AND b.created_at < '2022-01-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as three)
Is there a rule that keeps me from having more than two subqueries inside my from clause?
If so is there an other way of doing this? My real problem is for 12 month actually not 4. I already have a working solution but performance wise it's bad, that is why I am trying this.
My working solution is looking like that:
(SELECT
COALESCE(AVG(req.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as req)
UNION ALL
(SELECT
COALESCE(AVG(req.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-03-01' AND b.created_at < '2022-04-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as req)
UNION ALL
(SELECT
COALESCE(AVG(req.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-04-01' AND b.created_at < '2022-05-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as req)
UNION ALL
(SELECT
COALESCE(AVG(req.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-05-01' AND b.created_at < '2022-06-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as req)
Something like this (but I'm pretty sure I don't have the right way of getting months, years in postgres - you will have to fix that!)
SELECT
A.client_code,
avg(A.total) as AvgMonthlyTotal
FROM
(
SELECT
SUM(p.total) as total,
month(b.created_at),
p.client_code
FROM
bill b
INNER JOIN product p
on b.id = p.bill_id
WHERE
p.Category = 'FOOD'
and year(b.created_at) = 2022
GROUP BY
month(b.created_at)
p.client_code
) A
GROUP BY A.client_code;
Related
This question already has answers here:
MySQL SELECT JOIN with empty row returns
(2 answers)
Closed 1 year ago.
SQL QUERY:
SELECT
c.id AS c_id,c.image AS category_image,b.id AS p_id,b.image AS product_image,
a.id as product_id,0 AS sc_id,0 AS sub_category,0 AS is_active2, a.*,b.*,c.*
FROM stock_50644 a
JOIN product b ON a.product_id = b.id
JOIN category c ON b.category = c.id
WHERE b.category=1 ORDER BY p_id ASC
If stock_50644 has no product entry then i am getting empty result. How can i make it return all product with stock value null if stock is empty
EDIT:
PRODUCT ; STOCK_50644
id product_name category_id ; id product_id price
---- -------- ---------- --------------- ; ---- --------- ----- --------------
1 name1 1 ; 1 2 15
2 name2 2 ;
if i put WHERE b.id=1 in below query im getting correct expected output.
But as soon as i replace it by ORDER BY b.id ASC LIMIT 1; it is taking forever time and then #2013 - Lost connection to MySQL server during query
SELECT
c.id AS c_id, c.image AS category_image, b.id AS p_id, b.image AS product_image,
a.id AS product_id, 0 AS sc_id, 0 AS sub_category, 0 AS is_active2, a.*,b.*,c.*
FROM stock_50644 a
RIGHT JOIN product b ON a.product_id = b.id AND b.category = 1
LEFT JOIN category c ON b.category = c.id
WHERE b.id=1
ORDER BY b.id ASC LIMIT 1;
Move the restriction on the category from the WHERE clause to the ON clause of the join:
SELECT
c.id AS c_id, c.image AS category_image, b.id AS p_id, b.image AS product_image,
a.id AS product_id, 0 AS sc_id, 0 AS sub_category, 0 AS is_active2,
a.*,b.*,c.*
FROM product b
LEFT JOIN stock_50644 a
ON a.product_id = b.id AND b.category = 1
LEFT JOIN category c
ON b.category = c.id
ORDER BY p_id;
transactions table (1 row --> 1 transaction)
customer_code amount
A0BEFG 100
DEC21A 80
payment table (1 row --> 1 transaction)
customer_id payment_type
1 cash
2 credit_card
customer table (1 row --> 1 customer_code)
customer_code customer_id
A0BEFG 2
DEC21A 1
Expected output:
combined table
customer_code customer_id amount payment_type
AOBEFG 2 100 credit_card
DEC21A 1 80 cash
In words, my idea is to get the payment_type into the transactions table, but because there is no matching variable, I need to first merge payment table and customer table, before connecting them to the transactions table.
Code I've tried:
with
connection as (
select c.customer_code, c.customer_id, p. payment_type
from data.payment p
left join data.customer c on p.customer_id = c.customer_id
),
transactions as (
select t.merchant_code, t.amount
from data.transactions t
)
select
t.merchant_code, c.customer_id, c.amount, p.payment_type
from transactions as t
Code is for PostgreSQL.
SELECT c.customer_code, c.customer_id, t.amount, p.payment_type
FROM customer AS c
INNER JOIN payment AS p ON p.customer_id = c.customer_id
INNER JOIN transactions AS t ON t.customer_code = c.customer_code
Join customer to the other tables like this:
SELECT c.customer_code, c.customer_id, t.amount, p.payment_type
FROM transactions t
INNER JOIN customer c ON t.customer_code = c.customer_code
INNER JOIN payment p ON p.customer_id = c.customer_id
See the demo.
Results:
customer_code
customer_id
amount
payment_type
A0BEFG
2
100
credit_card
DEC21A
1
80
cash
I need to get the difference between two tables.
I need to compare Product, Qty & price columns from two tables and say if its new record or I need to mention which column value is changed.
Example Table A
Product | Qty | Price | Comments
A 20 500 xyz
B 50 200 xyz
C 90 100 abc
Example Table B
Product | Qty | Price | Comments
A 20 500 sd
B 70 200 cv
C 90 200 wsd
D 50 500 xyz
Currently I am using Expect which gives all new / mismatched rows.
select Product,Qty,Price
from TableB
except
select Product,Qty,Price
from TableA
Product | Qty | Price
B 70 200
C 90 200
D 50 500
But I need the result set like below
Product | Result
B Updated Qty
C Updated Price
D New
You can do this using LEFT JOIN:
SELECT b.Product,
b.Qty,
b.Price,
Result = CASE WHEN a.product IS NULL THEN 'New'
ELSE 'Updated: ' +
STUFF( CASE WHEN a.Qty != b.Qty THEN ',Qty' ELSE '' END +
CASE WHEN a.Price != b.Price THEN ',Price' ELSE '' END,
1, 1, '')
END
FROM TableB b
LEFT JOIN TableA a
ON a.Product = b.Product
WHERE a.Product IS NULL
OR a.Qty != b.Qty
OR a.Price != b.Price;
Example on SQL Fiddle
Not the most concise approach for sure, but readable and probably efficient:
SELECT B.Product,
Result = 'Updated Qty'
FROM TableB B
LEFT OUTER JOIN TableA A
ON B.Product = A.Product
WHERE A.Product IS NOT NULL
AND A.Qty <> B.Qty
AND A.Price = B.Price
UNION ALL
SELECT B.Product,
Result = 'Updated Price'
FROM TableB B
LEFT OUTER JOIN TableA A
ON B.Product = A.Product
WHERE A.Product IS NOT NULL
AND A.Price <> B.Price
AND A.Qty = B.Qty
UNION ALL
SELECT B.Product,
Result = 'Updated Qty and Price'
FROM TableB B
LEFT OUTER JOIN TableA A
ON B.Product = A.Product
WHERE A.Product IS NOT NULL
AND A.Price <> B.Price
AND A.Qty <> B.Qty
UNION ALL
SELECT B.Product,
Result = 'New'
FROM TableB B
LEFT OUTER JOIN TableA A
ON B.Product = A.Product
WHERE A.Product IS NULL
Demo
If you need to order the result you have to do that in an outer query like here.
I have following tables structure,
cust_info:
cust_id cust_name
1 nikhil
2 sam
bill_info:
bill_id cust_id bill_amount
7 1 10000
8 1 15000
9 2 6000
10 2 4000
paid_info:
paid_id cust_id paid_amount
11 1 5000
12 1 5000
13 2 5000
14 2 5000
now my output should display sum of bill made by customer and total amount paid by that customer and balance amount
output
cust_id total_bill total_paid balance
1 25000 10000 15000
2 10000 10000 0
where,
for example,
for cust_id = 2,
total_bill= 10000 + 15000
total_paid = 5000 + 5000
balance = total_bill - total_paid
what is convenient way to do this in sql? any sample query?
here's what i've already tried
SELECT distinct c.cust_id
, sum(b.bill_amount) as total_bill
, SUM(p.paid_amt) AS totalpaid,sum(b.bill_amount) - SUM(p.paid_amt) AS balance
FROM cust_info c
INNER JOIN bill_info b ON c.cust_id = b.cust_id
INNER JOIN paid_info p ON p.cust_id= b.cust_id group by p.cust_id;
SELECT
cust_info.cust_id,
cust_name,
bill_amount,
paid_amount,
bill_amount - paid_amount AS balance
FROM cust_info
INNER JOIN (
SELECT cust_id, SUM(bill_amount) bill_amount
FROM bill_info
GROUP BY cust_id
) bill_info ON bill_info.cust_id = cust_info.cust_id
INNER JOIN (
SELECT cust_id, SUM(paid_amount) paid_amount
FROM paid_info
GROUP BY cust_id
) paid_info ON paid_info.cust_id = cust_info.cust_id
demo
SELECT c.cust_id,
SUM(b.total_bill),
SUM(p.total_paid),
SUM(c.total_bill) - SUM(p.total_paid)
FROM
cust_info c
LEFT JOIN bill_info b ON (c.cust_id = b.cust_id)
LEFT JOIN paid_info p ON (c.cust_id = p.party_id)
GROUP
BY cust_info.cust_id
SELECT DISTINCT cust_info.cust_id,
sum(bill_amount) AS 'total_bill' ,
sum(paid_amount) AS 'total paid' ,
(SUM(bill_amount) - sum(paid_amount)) AS balance
FROM cust_info
INNER JOIN bill_info ON cust_info.cust_id = bill_info.cust_id
INNER JOIN paid_info ON cust_info.cust_id = paid_info.cust_id
GROUP BY cust_info.cust_id
I am trying to Group by to get count 1 if there is more than one in a category for a widget in a country.
To explain it better, here is the code that i have got so far:
select c.Name As Country, w.Description As WidgetName, cat.Description As Category,
COUNT (wc.Id)AS Clicks
from ManufacturerWidgets mw
join WidgetClicks wc on mw.Id = wc.ManufacturerWidgetId
join Products p on wc.ProductId = p.Id
join Countries c on mw.CountryId = c.Id
join Widgets w on w.Id = mw.WidgetId
join Categories cat on cat.Id = p.CategoryId
where mw.Enabled = 1 and mw.ManufacturerId = 17 and wc.CreatedAt >= '2013-01-01 00:00:00.000' and wc.CreatedAt <= '2013-07-05 23:59:00.000'
GRoup By wc.WidgetImpressionId, c.Name, w.Description, cat.Description
HAVING wc.WidgetImpressionId > 0
order by c.Name, w.Description, cat.Description
This returns for example:
County WidgetName Category Clicks
Austria onebysix computing 1
Austria onebysix computing 4
Austria onebysix printing 1
Austria onebysix printing 3
Austria onebysix printing 2
What I need to return is the inteeraction for each category so:
Austria onebysix computing 4 - this would be 1 not 4
and i need to group them by the category so the end result would be:
Austria onebysix computing 2
Austria onebysix printing 3
Remove wc.WidgetImpressionID from the GROUP BY. This is adding the extra level of grouping which happens to be the count for the desired result
Change the COUNT to COUNT(wc.WidgetImpressionID) to give 2 and 3 respectively
The HAVING should be HAVING COUNT(wc.WidgetImpressionID) > 0. In your code, it acts like a WHERE clause filter because it is in the GROUP BY (see my first point above)
select c.Name As Country, w.Description As WidgetName, cat.Description As Category,
COUNT (wc.WidgetImpressionID) AS Clicks
from ManufacturerWidgets mw
join WidgetClicks wc on mw.Id = wc.ManufacturerWidgetId
join Products p on wc.ProductId = p.Id
join Countries c on mw.CountryId = c.Id
join Widgets w on w.Id = mw.WidgetId
join Categories cat on cat.Id = p.CategoryId
where mw.Enabled = 1 and mw.ManufacturerId = 17 and wc.CreatedAt >= '2013-01-01 00:00:00.000' and wc.CreatedAt <= '2013-07-05 23:59:00.000'
GRoup By c.Name, w.Description, cat.Description
HAVING COUNT(wc.WidgetImpressionID) > 0
order by c.Name, w.Description, cat.Description