SQL Pivot column values - sql

I have tried following this and this(SQL Server specific solution) but were not helpful.
I have two tables, Product and Sale and I want to find how many products are sold on each day. But I want to pivot the table so that columns become the products name and each row will contain the amount of products sold for each day ordered by the day.
Simplified schema is as following
CREATE TABLE product (
id integer,
name varchar(40),
price float(2)
);
CREATE TABLE sale(
id integer,
product_id integer,
transaction_time timestamp
);
This is what I want
I only managed to aggregate the total sales per day per product but I am not able to pivot the product names.
select date(sale.transaction_date)
, product.id
, product.name
, count(product_id)
from sale inner join
product on sale.product_id = product.id
group by date(sale.transaction_date)
, product.id
, product.name
This is the situation so far
Please suggest.

You need pivoting logic, e.g.
select
s.transaction_date::date,
count(case when p.name = 'intelligent_rubber_clock' then 1 end) as intelligent_rubber_clock,
count(case when p.name = 'intelligent_iron_wallet' then 1 end) as intelligent_iron_wallet,
count(case when p.name = 'practical_marble_car' then 1 end) as practical_marble_car
from sale s
inner join product p
on s.product_id = p.id
group by
s.transaction_date::date;
Since your expected output aggregates by date alone, then only the transaction date should be in your GROUP BY clause. The trick used here is to take the count of a CASE expression which returns 1 when the record is from a given product, and 0 otherwise. This generates conditional counts for each product, all in separate columns. To add more columns, just add more conditional counts.

Related

Does it matter to filter results when doing aggregation?

I want to get my sales for each day which is located in my orders_summary table.
orders_summary table columns: id, date, amount, sku_id
products table columns: id, sku
Currently Im getting my daily sales like this:
SELECT
MAX(CASE WHEN os.date = '01/01/2022' THEN COALESCE(amount,0)::INT ELSE 0 END) AS orders_1,
MAX(CASE WHEN os.date = '01/02/2022' THEN COALESCE(amount,0)::INT ELSE 0 END) AS orders_2
FROM products AS p
LEFT JOIN orders_summary AS os ON p.id = os.sku_id
WHERE p.id = '1'
GROUP BY p.id;
Is it important to add AND date BETWEEN '01/01/2022' AND '01/02/2022' in my where clause?
Yes absolutely. Imagine having 10 years worth of data in the table where you're only interested in the data for two days. You must use the where clause which restricts the number of rows (down to 0.05% in this case) before doing the group by.

SQL query with grouping

I have a problem by solving following task:
'Show for every seller how much he earned (quantity * product_price) by selling the product PS4 in the year 2013'
The relations are:
seller(id , seller_name, advertised_by);
product( id, product_name, product_price);
sale(id, seller_id, product_id, quantity, date);
I inserted following data:
INSERT into seller VALUES
(1,'Bob',NULL),
(2,'Mary',1),
(3,'Peter',1),
(4,'Parker',1),
(5,'Jeff',1);
INSERT INTO product VALUES
(1,'PS4',100),
(2,'XBOX One',300),
(3,'Laptop',500);
INSERT INTO sale VALUES
(1,1,1,1,'4 5 2013'),
(2,2,1,2,'5 6 2013'),
(3,3,1,3,'6 6 2013'),
(4,4,1,4,'6 6 2013');
I know not using foreign keys or using varchar for date isn't good but I want to have the example being simple.
SELECT seller.id,seller.seller_name, (sale.quantity * product.price) AS sale
FROM seller,product,sale
WHERE product.id = sale.product_id
AND product.product_name = 'PS4'
AND sale.date like '%2013'
GROUP by seller.id;
I know that I have to use a GROUP BY but grouping by seller.id doesn't work.
You need to group by every column that isn't aggregated, and apply an aggregate function to the others. Here, you need to add sellar_name to the group by clause (which shouldn't change the grouping, as the id is already unique), and sum the sales.
Also, as a side note, using implicit joins (having more than one table in the from clause) has been deprecated for several years, and it's recommended you use an explicit join instead:
SELECT seller.id,seller.seller_name, SUM(sale.quantity * product.price) AS sale
FROM seller
JOIN sale ON sale.seller_id = seller.id
JOIN product ON product.id = sale.product_id
WHERE product.product_name = 'PS4' AND sale.date like '%2013'
GROUP BY seller.id;

How to count each instance where a certain product was sold after a different product? SQL or DAX

Sorry if the title seems confusing, it was the best I could come up with.
I can work with both excel(Dax since its a power query) and sql:
I have a situation where there are two product types being purchased, Type_A and Type_B.
I want to calculate a count of how many unique Loc_ID have purchased a "Type_A" Product type, AFTER purchasing a "Type_B" Product type.
From my example there are a total of 3 unique Loc_ID which would fall in this filter: Loc_01, Loc_02, and Loc_04
Any help is greatly appreciated
Try this (it works good if each loc_id purchased both type of products as in your example.
select count(*)
from
(select loc_id , max(date_purchased) dt
from table t where product_type = 'type_a'
group by loc_id) a,
(select loc_id , max(date_purchased) dt
from table t where product_type = 'type_b'
group by loc_id) b
where a.loc_id=b.loc_id and a.dt>b.dt;
This will work even if certain loc_id did not purchase both type of products
Try this:-
Select count(a.loc_id) as cnt_locations
from
your_table_name a
inner join
(
Select a.loc_id,b.date_purchased,b.Product_type
from
(
Select loc_id, min(date_purchased) as date_purchased
from
your_table_name
group by loc_id
) a
inner join
your_table_name b
on a.loc_id=b.loc_id and a.date_purchased =b.date_purchased
where Product_type ='Type_B'
) b
on
a.loc_id=b.loc_id
where a.date_purchased >b.date_purchased and a.Product_type ='Type_A'

PostgreSQL sum quantity of children items

I have a subscription service that delivers many items.
Subscribers add items to a delivery by creating a row in delivery_items.
Until recently subscribers could only add 1 of each item to a delivery. But now I have added a quantity column to my delivery_items table.
Given this schema, and an outdated query (on SQL Fiddle), how can I select the total amount of an item I will need for each day's deliveries?
This provided a table of days, and items being delivered that day but doesn't account for quantity:
SELECT
d.date,
sum((di.item_id = 1)::int) as "Bread",
sum((di.item_id = 2)::int) as "Eggs",
sum((di.item_id = 3)::int) as "Coffee"
FROM deliveries d
JOIN users u ON u.id = d.user_id
JOIN delivery_items di ON di.delivery_id = d.id
GROUP BY d.date
ORDER BY d.date
Ideally, my query would be agnostic to the specifics of the items, like the id/name.
Thanks
Edit to add schema:
deliveries (TABLE)
id int4(10)
date timestamp(29)
user_id int4(10)
delivery_items (TABLE)
delivery_id int4(10)
item_id int4(10)
quantity int4(10)
items (TABLE)
id int4(10)
name varchar(10)
users (TABLE)
id int4(10)
name varchar(10)
You don't need to JOIN your users table, because you're neither getting any data from it nor using it as your joining condition.
Here's your edited SQL Fiddle
Using conditional sum() function would retrieve values of needed goods to deliver for a particular date.
SELECT
d.date,
sum(CASE WHEN di.item_id = 1 THEN di.quantity ELSE 0 END) as "Bread",
sum(CASE WHEN di.item_id = 2 THEN di.quantity ELSE 0 END) as "Eggs",
sum(CASE WHEN di.item_id = 3 THEN di.quantity ELSE 0 END) as "Coffee"
FROM deliveries d
JOIN delivery_items di ON di.delivery_id = d.id
GROUP BY d.date
ORDER BY d.date
You could also look into crosstab(text, text) function. Result would be the same, but you can also specify query that produces the set of categories.
Though, if you want to get dynamic results when your items table has additional rows, you would need to wrap this up in a function and build the output columns and types definition, because:
The crosstab function is declared to return setof record, so the actual names and types of the output columns must be defined in the FROM clause of the calling SELECT statemen

SQL Query (max of sum per year)

I will try to describe my problem here. Let's say that i have 3 tables:
1. sales
salesID (PK)
productID(FK) - FK that points to the sold product
storeID(FK) - FK that points to the store that sold the product
month - month in which the product was sold
year - year in which the product was sold
amount - amount(of money) for which the product was sold
2. product
productID (PK)
productName
3. store
storeID (PK)
storeName
I need the following query: For every store show the top selling product in that store for the specified year. So the result should look something like:
STORE PRODUCT AMOUNT
store1 product1 XXX amount
store2 product2 YYY amount
store3 product1 XYX amount
Where each amount would be the highest sum of all amounts in that year.
What I can do now is:
SELECT store.storeName
, product.ProductName
, SUM(sales.ammount)
FROM sales
JOIN product ON sales.productID = product.productID
JOIN store ON sales.storeID = store.storeID
GROUP BY store.storeName
, product.ProductName
WHERE sales.year = 'XXXX'
;
Where I get a sum per product and per store. After this I can use the cursor to go thru the entire table row by row and check which one has the highest amount for that store.
But I was wondering if it is possible to do that in a 'simple' query?
I'm using SQL Developer for Oracle 11g database.
And I would appreciate any help.
Not terribly familiar with Oracle, but perhaps something like:
WITH cte AS (SELECT store.storeName
, product.ProductName
, SUM(sales.ammount) as sales_amount
FROM sales
JOIN product ON sales.productID = product.productID
JOIN store ON sales.storeID = store.storeID
WHERE sales.year = 'XXXX'
GROUP BY store.storeName, product.ProductName
)
SELECT a.*
FROM cte a
JOIN (SELECT storeName
, MAX(sales_amount) as Max_Sales
FROM cte
GROUP BY storeName
)b
ON a.storeName = b.StoreName
AND a.sales_amount = b.Max_Sales;
If there's a tie that returns both entries, you can use ROW_NUMBER() instead to limit to one result per storeName .