SQL Select Statement with nested group by clause - sql

I have 2 tables. I need to select the column name and a calculated field from Invoices called balance_due.
The result of the query should be the name and their balance due from all of their records combined.
Thanks for any help.

SELECT v.vendor_name, i.totalbalance
FROM Vendors as v
INNER JOIN (
SELECT vendor_id, sum(invoice_total-payment_total) as totalbalance
FROM invoices
GROUP BY vendor_id
) as i on i.vendor_id = v.vendor_id

Or there is another syntax:
;With i As
(
SELECT vendor_id, sum(invoice_total-payment_total) as totalbalance
FROM invoices
WHERE payment_total is not null
GROUP BY vendor_id
)
SELECT Vendors.vendor_name, i.totalbalance
From Vendors LEFT JOIN i ON Vendors.vendor_id = i.vendor_id

Related

All joined subquery results return null

I am trying to get all customers with their latest payment transaction, including customers without any transaction:
SELECT c.customer_id, c.phone_number, c.email
, p.transaction_no, p.amount, p.transaciton_datetime
FROM tbl_customers c
LEFT JOIN (
SELECT customer_id, transaction_no, amount, transaciton_datetime
FROM tbl_payment_transactions
ORDER BY payment_transaction_id DESC
LIMIT 1
) p
ON c.customer_id = p.customer_id
The above query returns NULL for p.transaction_no, p.amount, p.transaciton_datetime in every row. But I can make sure that there are transactions made by customers in tbl_payment_transactions.
You want the subquery to be run once per each different row of the driving table tbl_customers. This is called a lateral subquery and takes the form:
SELECT
c.customer_id, c.phone_number, c.email,
p.transaction_no, p.amount, p.transaciton_datetime
FROM tbl_customers c
LEFT JOIN LATERAL (
SELECT customer_id, transaction_no, amount, transaciton_datetime
FROM tbl_payment_transactions t
WHERE c.customer_id = t.customer_id
ORDER BY payment_transaction_id DESC
LIMIT 1
) p
ON true
The Impaler provided the correct form with a LATERAL subquery.
Alternatively, you can use DISTINCT ON in a subquery and a plain LEFT JOIN.
Performance of the latter can be better while retrieving all (or most) customers, and if there are only few transactions per customer and/or you don't have a multicolumn index on (customer_id, payment_transaction_id) or (customer_id, payment_transaction_id DESC):
SELECT c.customer_id, c.phone_number, c.email
, p.transaction_no, p.amount, p.transaciton_datetime
FROM tbl_customers c
LEFT JOIN (
SELECT DISTINCT ON (customer_id)
customer_id, transaction_no, amount, transaciton_datetime
FROM tbl_payment_transactions
ORDER BY customer_id, payment_transaction_id DESC
) p USING (customer_id);
About performance aspects:
Optimize GROUP BY query to retrieve latest row per user
Select first row in each GROUP BY group?

Getting a SUM of the values in INNER JOIN adds up duplicate values

I am running a query which is counting the records on monthly basis from the table.
I am trying to add one extra column called "TotalPrice", I need a sum of all the prices from 'settle' table.
The problem I am facing is because of INNER JOIN, 'SUM' of the prices is adding up multiple prices due to duplicate records which the INNER JOIN is returning. Is there a way to avoid it and get a SUM of the prices from unique records ?
SELECT
CONCAT(year(datetime), '-', month(datetime)) AS YearMonth,
COUNT (DISTINCT a.id) AS TOTAL, SUM(total_price) AS TotalPrice
FROM settle AS a with (nolock)
INNER JOIN transfers b with (nolock) ON b.settleId = a.id
INNER JOIN Fdata AS c with (nolock) ON c.id= b.data
GROUP BY CONCAT(year(datetime), '-', month(datetime))
Thanks in advance.
sql server 2008 onwards:
with CTE as -- A CTE alows us to manipulate the data before we use it, like a derived table
(
select datetime, id, total_price,
row_number() over(partition by id, datetime order by total_price) as rn -- This creates a row number for each combo of id and datetime that appears
FROM settle AS a with (nolock)
INNER JOIN transfers b with (nolock) ON b.settleId = a.id
INNER JOIN Fdata AS c with (nolock) ON c.id= b.data
)
SELECT CONCAT(year(datetime), '-', month(datetime)) AS YearMonth,
COUNT (DISTINCT a.id) AS TOTAL,
SUM(total_price) AS TotalPrice
from CTE
where rn = 1 -- that row_number we created? This selects only the first one, removing duplicates
group by CONCAT(year(datetime), '-', month(datetime))

use IN statement within ID column and retrieve count(*)?

I have the following SQL command:
SELECT * from products
WHERE id IN (
SELECT product_id, count(*)
FROM account_products
GROUP BY product_id
)
obviously it doesnt retrieve any data, because the internal query retrieves two columns (product_id and count).
But I need the count too, because I'm gonna use it to make some math later.
How can I use this IN query using the count(*) too?
Thanks!
Join them:
select products.*, t.product_count
from products
join (
SELECT product_id, count(*) as product_count
FROM account_products
GROUP BY product_id
) as t on t.product_id = products.id
select products.*, count( account_products.product_id)
from products join account_products on
products.product_id = products.id
group by products.* (obviously all products-fields)
Or with a subquery in the select clause, when your DBMS supports it:
SELECT
products.*,
(SELECT count(*) FROM account_products ap WHERE ap.product_id = products.id) as "Number of Accounts"
from products
This might work for you:
WITH MyAccountProducts AS
(
SELECT product_id, count(*) AS CountOfAccountProducts
FROM account_products
GROUP BY product_id
)
SELECT p.*,ap.CountOfAccountProducts
from products AS p
INNER JOIN MyAccountProducts AS ap ON p.id=ap.product_id

Row value from another table

I have a table that is having 2 duplicate rows (total of 3 rows), so I used the code below to get the duplicate value in the column
SELECT CustNo, COUNT(*) TotalCount
FROM Rental
GROUP BY CustNo
HAVING COUNT(*) > 1
ORDER BY COUNT(*) DESC
So once I get the repeated value, I need to get the CustNo derived as duplicate from the customer table. How do I go about taking this value and using it in the select statment all in the same query.
I also have the select statement prepared like this.
Select * from Customer where CustNo = 'T0002';
Thanks.
Select * from Customer
where CustNo IN
(
SELECT CustNo
FROM Rental
GROUP BY CustNo
HAVING COUNT(*) > 1
)
You can use join:
SELECT c.*
FROM (SELECT CustNo, COUNT(*) TotalCount
FROM Rental
GROUP BY CustNo
HAVING COUNT(*) > 1
) cc JOIN
Customer c
on cc.CustNo = c.CustNo;
Select C.* from Customer C RIGHT JOIN (
SELECT CustNo
FROM Rental
GROUP BY CustNo
HAVING COUNT(*) > 1) D
ON C.CustNo = D.CustNo
You can also try this,
With tblDups as(
select CustNo,count(CustNo) as TotalCount from a_rental
Group by CustNo
Having count(CustNo) >1)
select b.* from a_rental b
inner join tblDups a on a.CustNo = b.Custno

sql query with two tables having reference key

I have two tables:
Discounts(disid primary key)
Cust(custid primary key, disid ref discount(disid))
Now I need a query to get custid having all disid(discount coupons) and the customer may contain the same disid more than once.
select custid, count(distinct disid) from cust
group by custid
having count(*) = (select count(*) from discounts)
SELECT COUNT(DISTINCT D.disid) FROM CUST C
INNER JOIN DISCOUNTS D ON D.disid=C.disid GROUP BY D.disid
try either of this solutions:
SELECT a.custid, COUNT(a.disid) totalCoupon
FROM cust a
INNER JOIN discounts b
ON b.disid = a.disid
GROUP BY a.custid
or
SELECT a.custid, COUNT(a.disid) totalCoupon
FROM cust a
INNER JOIN discounts b
ON b.disid = a.disid
GROUP BY a.custid
HAVING COUNT(a.disid) > 1 -- customers having the same (but more than 1)
-- CouponID will only be shown here