How do I query this case? - sql

I have three tables.
Category(id, name);
Item(id, name, category);
SoldItem(id, item, qty);
My goal is simple, I want to list ALL the categories plus item count of those categories plus the count of sold items of the items of those particular categories as rows.
The result would be like this:
Category | Item count | Item sold count
----------------------------------------
Food | 2 | 50
----------------------------------------
Beverage | 3 | 60
How do I query this in PostgreSQL. Thanks in advance.

The key is to use count(distinct) when counting rows from the middle joined table, otherwise multiple grandchildren would result in the same patent being counted multiple times too:
select
c.name as category,
count(distinct i.id) as item_count,
sum(si.qty) as item_sold_count
from Category c
left join Item i on i.category = c.id
left join SoldItem si on si.item = i.id
group by c.name;
By using left joins, categories without items, and items with sales, will still show but with zeros for the totals.

Related

How can I sum data from mulitple rows which have the same foreign key into one row?

I have two tables which have a 1 to n relation. One table contains general Information of a bill (named bill)
(< -1 to n ->)
and the other contains Items which are on the bill (named items). I want a query that Lists all Bills and sums up the prices from the items in a new row. But of course i want every Bill listed just once not for every item.
Usually i don't post anything. But i can't find an answer because i don't know how to search for this problem. Sorry when this is obvious.
What my tables look like:
bill:
bill_id - customer - date
items:
item_id - bill_id - amount - price
A simple join with aggregation should work here:
SELECT
b.bill_id,
COALESCE(SUM(i.price), 0) AS total_price
FROM bill b
LEFT JOIN items i
ON b.bill_id = i.bill_id
GROUP BY
b.bill_id;
If you want to include the other two columns from the bill table, then just add them to the SELECT and GROUP BY clauses.
You may try this.
; with cte as (
select b.bill_id, i.item_id ,isnull(i.price,0) as Price from
Bill as b inner join items as i on b.bill_id =i.bill_id
union all
select b.bill_id , null, sum(isnull(i.price,0)) from
Bill as b inner join items as i on b.bill_id =i.bill_id
group by b.bill_id
)
select * from cte order by bill_id, item_id desc

Selecting data from 3 tables to calculate inventory levels

I am currently setting up an inventory system in phpgrid. I have 3 tables - products, orders, purchases. I am trying to list all of the PartNumber field from products and the total of the NumberReceived for each PartNumber from purchases along with the total of the NumberShipped for each PartNumber from orders.
From there I believe I can use phpgrid to display a virtual/calculated column that will subtract the NumberShipped from the NumberReceived and give me the current stock level of each PartNumber.
Looking for results something like this:
PartNumber | Received | Shipped | (calculated) Qty On Hand
______________________________________________________________
12345 | 20 | 10 | 10
67890 | 40 | 5 | 35
I am having trouble wrapping my head around how to join everything together to retrieve the data that I want. This SELECT displays some data but not every PartNumber with the proper calculations.
"SELECT
p.id,
sum(p.NumberReceived) AS 'Received',
sum(o.NumberShipped) As 'Shipped'
FROM purchases p
INNER JOIN orders o on p.id = o.ProductId
GROUP BY p.id ",
"id", "purchases");
I realized that I need to have the PartNumber column in there in order to list all the data of the PartNumber field. So then I tried this - I think I'm close but this keeps giving me an error - PHPGRID_ERROR: Could not execute query. Error 102
"SELECT
prod.id,prod.PartNumber,
sum(pur.NumberReceived) AS 'Received',
sum(o.NumberShipped) As 'Shipped',
FROM products prod
LEFT JOIN orders o ON prod.PartNumber = o.ProductId
LEFT JOIN purchases pur ON prod.PartNumber = pur.ProductId",
"id", "products");
I'm hoping someone could help steer me in the right direction. Thank you all!
If you are summing you need to group by. The following should work. If not, send specific error message.
(you also had an extra comma after the last item in the select block)
SELECT
prod.id,prod.PartNumber,
sum(pur.NumberReceived) AS Received,
sum(o.NumberShipped) As Shipped
FROM products prod
LEFT JOIN orders o ON prod.PartNumber = o.ProductId
LEFT JOIN purchases pur ON prod.PartNumber = pur.ProductId"
GROUP BY prod.id, prod.PartNumber;

Aggregate after join without duplicates

Consider this query:
select
count(p.id),
count(s.id),
sum(s.price)
from
(select * from orders where <condition>) as s,
(select * from products where <condition>) as p
where
s.id = p.order;
There are, for example, 200 records in products and 100 in orders (one order can contain one or more products).
I need to join then and then:
count products (should return 200)
count orders (should return 100)
sum by one of orders field (should return sum by 100 prices)
The problem is after join p and s has same length and for 2) I can write count(distinct s.id), but for 3) I'm getting duplicates (for example, if sale has 2 products it sums price twice) so sum works on entire 200 records set, but should query only 100.
Any thoughts how to sum only distinct records from joined table but also not ruin another selects?
Example, joined table has
id sale price
0 0 4
0 0 4
1 1 3
2 2 4
2 2 4
2 2 4
So the sum(s.price) will return:
4+4+3+4+4+4=23
but I need:
4+3+4=11
If the products table is really more of an "order lines" table, then the query would make sense. You can do what you want by in several ways. Here I'm going to suggest conditional aggregation:
select count(distinct p.id), count(distinct s.id),
sum(case when seqnum = 1 then s.price end)
from (select o.* from orders o where <condition>) s join
(select p.*, row_number() over (partition by p.order order by p.order) as seqnum
from products p
where <condition>
) p
on s.id = p.order;
Normally, a table called "products" would have one row per product, with things like a description and name. A table called something like "OrderLines" or "OrderProducts" or "OrderDetails" would have the products within a given order.
You are not interested in single product records, but only in their number. So join the aggregate (one record per order) instead of the single rows:
select
count(*) as count_orders,
sum(p.cnt) as count_products,
sum(s.price)
from orders as s
join
(
select order, count(*) as cnt
from products
where <condition>
group by order
) as p on p.order = s.id
where <condition>;
Your main problem is with table design. You currently have no way of knowing the price of a product if there were no sales on it. Price should be in the product table. A product cost a certain price. Then you can count all the products of a sale and also get the total price of the sale.
Also why are you using subqueries. When you do this no indexes will be used when joining the two subqueries. If your joins are that complicated use views. In most databases they can indexed

Select count of column grouped by another column SQL

I have two tables, one called Products and one called Products_Category. I want to show the inventory count for each category (grouped by category), and also display the name of the category. The Products table has the inventory count for each Product, but the Products_Category table has the Category names. How can I display the two together? Should I be using some kind of join?
Here's what I'm trying to get:
Category_Name --------- Sum(Products_Inventory)
------Books -------------------- 1 ----------
------Toys---------------------- 2 ----------
But as of right now all I'm able to display is the category ID instead of the category name, like so:
------- 1 ------------------------ 1 ----------
------- 2 ------------------------ 2 ----------
Use JOIN and GROUP BY:
SELECT PC.Category_Name, Sum(P.Products_Inventory)
FROM Products_Category PC
INNER JOIN Products P ON PC.ProductId = P.ProductId
GROUP BY PC.Category_Name
BTW -- This assumes you have a ProductId field in each table. And if I could make a suggestion, you should probably have a Category table as well (better for normalization). Store your ProductId and CateogryId in your Products_Category table.
Good luck.
For these queries I assume product_id is on both tables (you'll have to use the correct field name if I guessed wrong). I'm also using Product_Category.description as the name of the field that has the category names.
This query will get you all categories, and a count of 0 for any product category that's not found in the Product table.
If Product contains one record for each product in stock (ie, 4 books would mean 4 Product records), than COUNT() is the function you want.
SELECT Products_Category.description, COUNT(Product.product_id) AS category_count
FROM Products_Category
LEFT OUTER JOIN Products ON Products_Category.product_id=Products.product_id
GROUP BY Products_Category.description
If Product contains one record for each type product in stock (ie, 4 books would mean 1 record with a product_count of 4), than SUM() is the function you want.
SELECT Products_Category.description, SUM(Product.product_count) AS category_count
FROM Products_Category
LEFT OUTER JOIN Products ON Products_Category.product_id=Products.product_id
GROUP BY Products_Category.description
If you only want records returned when you have inventory in that category, change the LEFT OUTER JOIN to INNER JOIN:
SELECT Products_Category.description, SUM(Product.product_count) AS category_count
FROM Products_Category
INNER JOIN Products ON Products_Category.product_id=Products.product_id
GROUP BY Products_Category.description
the most simplest way is to do this..
SELECT a.Category_Name, Sum(b.Products_Inventory)
FROM Products_Category a, Products b
Where a.ProductId = b.ProductId
GROUP BY a.Category_Name
hope it helps

SQL Select statement which shows row-specific count information?

I need some SQL help ..
Suppose I have 2 tables: Customers and Products.
Now, I want to see a SQL statement that will show me these two columns:
Customer | Number of orders placed
How do I do that ?
The second column is a non-existent column, which is showing a number which states how many orders that customer has placed.
For example:
Customer | Number of orders placed
-------- | -----------------------
John | 23
Jack | 5
Mary | 12
etc ..
What's the SQL for this kind of a select ?
I guess that the Product table contains a foreign key CustomerID which references the Customer. The resulting query would be
select Customers.Name, Count(*)
from Customers join Products
on Customers.CustomerID = Products.CustomerID
However, this is just a guess as you forgot to inform us about the relation between the two tables, i.e. how the Products know to which Customer they belong.
Also, but this is a bit picky, you want the number of orders but only have a 'Product' table...
JOIN. This is just making up your column names since tables weren't given.
SELECT
c.Name,
myOrders = COUNT(o.id)
FROM Customers c
INNER JOIN Orders o
ON c.id = o.customerId
GROUP BY c.Name
Some quick reading: JOINS. GROUP BY