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

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

Related

SQL: Order by count return only the first row

I have two tables products and product_views.
I want to write a query that returns the products with the largest number of views.
products table has id and name columns. product_views has columns id, customer_id, product_id. The counting is based on the product_id column.
Here is my query:
SELECT *
FROM products
LEFT JOIN product_views ON products.id = product_views.product_id
ORDER BY COUNT(product_views.product_id) DESC;
The problem is it returns only the first product. I think the issue is that initially the product_views table is all empty (all values are null).
How to solve this issue?
Based on your criteria, the following should work -
SELECT
p.id,
p.name,
count(v.product_id) as views
FROM products p
LEFT JOIN product_views v
ON p.id = v.product_id
GROUP BY 1,2
ORDER BY views DESC;
The query ignores customer_id, and counts product_ids based on the id/name groupings.
Calculates count in separate script:
SELECT products.id, products.name, pv.count FROM products
JOIN (SELECT product_id, COUNT(*) as count FROM product_views GROUP BY product_id)
pv ON (products.id = product_id )
ORDER BY pv.count DESC;
Example: SQL Fiddle

All combinations of counts

I have a table, with columns
product_id, status, for example:
product_id
status
1
ok
2
bad
1
ok
3
bad
2
bad
1
ok
I'd like to show count of all possible combinations of product_ID and status:
product_id
status
count
1
ok
3
1
bad
0
2
ok
0
2
bad
2
3
ok
0
3
bad
1
The solution I've found is that I could use a Cartesian join and then union it with regular counts and aggregate the results (works fine):
SELECT product_id, status, SUM(cnt) FROM (
---all combinations, no count
SELECT DISTINCT t1.product_id, t2.status, 0 AS cnt
FROM
details t1,
details t2
UNION
---counts of existing combinations
SELECT DISTINCT product_id, status, COUNT(status) AS cnt
FROM details
GROUP BY product_id, status) AS T1
GROUP BY product_id, status
Now I am wondering, is here a better way to do it?
I learning SQL with PostgreSQL and Access SQL. Comments are added to clarify (left-out in Access code).
Use CROSS JOIN to build all combinations and top up with a LEFT JOIN:
SELECT p.product_id, s.status, COUNT(t.any_not_null_column)
FROM (SELECT DISTINCT product_id FROM t) AS p
CROSS JOIN (SELECT DISTINCT status FROM t) AS s
LEFT JOIN t ON p.product_id = t.product_id AND s.status = t.status
GROUP BY p.product_id, s.status
The following is a Postgres solution (a database I strongly recommend over MS Access). The idea is to generate all the rows and then use left join and group by to get the counts you want:
select p.product_id, s.status, count(d.product_id)
from (select distinct product_id from details) p cross join
(values ('ok'), ('bad')) s left join
details d
on d.product_id = p.product_id and d.status = s.status
group by p.product_id, s.status;
Note: You might have other tables that have the list of products and/or statuses that you want.
An equivalent version in MS Access (which would also work in Postgres) might look like:
select p.product_id, s.status, count(d.product_id)
from ((select distinct product_id from details) p,
(select distinct status from details) s
) left join
details d
on d.product_id = p.product_id and
d.status = s.status
group by p.product_id, s.status;

MS Access multiple joins with criteria

I'm generating an Inventory Query and the below code (mostly) works but it includes Invoices that have been voided, resulting in negative inventory.
Void Yes/No field = tblInvoices.Void
tblInvoiceDetails.InvoiceNum = tblInvoices.ID
I can't figure out how to ensure this does not include invoices that were voided. Thank you in advance for your help!
SELECT tblInventory.ID, Nz(sumTotalPaid,0)-Nz(sumCreditAmount,0) AS Quantity
FROM (tblInventory
LEFT JOIN (
SELECT ProductID, Sum(Quantity) AS sumTotalPaid
FROM tblOrderDetails
GROUP BY tblOrderDetails.ProductID
) AS sum1
ON tblInventory.ID = sum1.ProductID)
LEFT JOIN (
SELECT ProductID, Sum(Quantity) AS sumCreditAmount
FROM tblInvoiceDetails
GROUP BY tblInvoiceDetails.ProductID
) AS sum2
ON tblInventory.ID = sum2.ProductID;
Try it this way:
SELECT tblInventory.ID, Nz(sumTotalPaid,0)-Nz(sumCreditAmount,0) AS Quantity
FROM (tblInventory
JOIN (
SELECT ProductID, Sum(Quantity) AS sumTotalPaid
FROM tblOrderDetails
GROUP BY tblOrderDetails.ProductID
) AS sum1
ON tblInventory.ID = sum1.ProductID)
JOIN (
SELECT ProductID, Sum(Quantity) AS sumCreditAmount
FROM tblInvoiceDetails
WHERE tblInvoiceDetails.InvoiceNum IN
(SELECT tblInvoices.ID
FROM tblInvoices
WHERE tblInvoices.Void='Yes')
GROUP BY tblInvoiceDetails.ProductID
) AS sum2
ON tblInventory.ID = sum2.ProductID
1.- First you use just JOIN instead of LEFT JOIN, so you just get the rows that have values in both tables.
2.- You get only the Products that have the tblInvoices.Void='Yes'

SQL nested aggregate grouping error

I'm trying to find name of product which has sold maximum units, I've two tables, purchases and products, products has pname and pid, purchases has pid, qty(units sold).
I've managed this
select p.pname, sum(q.qty) from purchases q
inner join products p on p.pid=q.pid
where p.pid=q.pid
group by p.pname
order by sum(q.qty) desc
I'm getting the result in descending order but I need only the top most selling units, multiple products can have top most selling units. When I use
max(sum(q.qty))
I get grouping error.
One approach is to derive the values first using a common table expression.
Simply put you can't wrap aggregates in other aggregates. You may be able to wrap an aggregate around an analytic however.
with cte as (select p.pname, sum(q.qty) from purchases q
inner join products p on p.pid=q.pid
where p.pid=q.pid
group by p.pname
order by sum(q.qty) desc)
Select pname, max(purchases)
from cte
group by pname
You can use ctes to do this.
1)First get the total quantity of each product
2)Then get the maximum of all those totals
3)Join it with your original query
with totals as (select pid, sum(qty) totalqty from purchases group by pid)
, t1 as (select p.pid, p.pname, sum(q.qty) totqty
from purchases q
inner join products p on p.pid=q.pid
group by p.pname)
, t2 as (select max(totalqty) maxtotal from totals)
select pname, totqty
from t1
join t2 on t1.totqty = t2.maxtotal
Analytics can simplify this for you. If you have more than one product with the same sum(qty) and that happens to be the max(sum(qty)), then this should get you them:
select pname, quantity
FROM (
select p.pname
, sum(q.qty) quantity
,rank() over (order by sum(q.qty desc) ranking
from purchases q
inner join products p on p.pid=q.pid
group by p.pname
)
where ranking = 1

SQL Select Statement with nested group by clause

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