How to query the Gross Revenue group by STORE, by item's CATEGORY for each days in year? [closed] - sql

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have SALES table of each days, its has an STOREID and the ITEMID with the DATE and GROSS
| DATE | STOREID | ITEMID | GROSS |
|------------ |--------- |-------- |------- |
| 2020-07-07 | STORE1 | ITEM1 | 10000 |
| 2020-07-07 | STORE2 | ITEM1 | 25000 |
| 2020-07-06 | STORE2 | ITEM3 | 15000 |
| 2020-07-06 | STORE3 | ITEM2 | 21000 |
The PRODUCT table show the category of the items, we have 5 items with 5 categories:
| ITEMID | CATEGORY |
|-------- |---------- |
| ITEM1 | A |
| ITEM2 | B |
| ITEM3 | C |
| ITEM4 | B |
| ITEM5 | D |
How can I select the revenue by storeid, itemid of each day with all of the category (if that day only sold category A, B then category C, D will show with gross is 0). Here the example of expected result when selecting the gross of July 7 for STORE1:
| DATE | STOREID | CATEGORY | GROSS |
|------------ |--------- |---------- |------- |
| 2020-07-07 | STORE1 | A | 10000 |
| 2020-07-07 | STORE1 | B | 0 |
| 2020-07-07 | STORE1 | C | 0 |
| 2020-07-07 | STORE1 | D | 0 |
I have tried:
SELECT distinct T.DATE, T.STOREID, P.CATEGORY, ISNULL(T.GROSS,0) AS GROSS
FROM PRODUCT P LEFT JOIN (
SELECT CONVERT(DATE, DATEID) AS DATE, STOREID, P.CATEGORY, convert(numeric(10,0), sum(S.GROSS)) AS GROSS
FROM SALES S join PRODUCT P on S.ITEMID = P.ITEMID
WHERE DATEID = '2020-07-07' and STOREID = 'STORE1'
GROUP BY P.CATEGORY, DATEID, STOREID
) T
ON P.CATEGORY = T.CATEGORY
The results I get is something like this:
| DATE | STOREID | CATEGORY | GROSS |
|------------ |--------- |---------- |------- |
| 2020-07-07 | STORE1 | A | 10000 |
| 2020-07-07 | STORE1 | B | 0 |
| NULL | NULL | C | 0 |
| NULL | NULL | D | 0 |
So when I execute the Query for others STORES and others days, how can I automated specify the correct value for the NULL value (like the expected result)
Thank you guys so much for your help!

Is this homework? It looks a bit like homework, so instead of writing you a query, I will give you a strategy to think about.
You will first have to generate a cartesian product of date, and category, and store. Then find the sales that apply to each combination of {date, category, store}.
In general, generating "all of the dates you care about" is easy with a calendar table, or tally table, but in your specific case you could also generate all the dates you need for the cartesian product by querying your sales table for the distinct dates. This solution won't always work, because what if you want an output row for a date where no sales happened?
A cartesian product in sql is generated using a cross join
So, the approach you want to take:
Get a set of all the dates you need in the output
Cross join that against the set of all categories you need in the output
Cross join that against the set of all stores you need in the output
Left join that to the sales table

Related

SQL - How to get latest price based on effective date?

Fairly straight forward query that is eluding me.. how do I get the effective cost for each product based upon the latest effective date given 7-6-2020 as the effective date?
Price Table:
id | product_name | cost | effective_date
=======================================================
1 | Product A | 8.00 | 1-5-2020
1 | Product A | 9.50 | 1-6-2020
1 | Product A | 10.00 | 1-7-2020
2 | Product B | 4.00 | 5-6-2020
2 | Product B | 4.50 | 5-7-2020
Expected Result:
id | product_name | cost | effective_date
-----------------------------------------------
1 | Product A | 10.00 | 1-7-2020
2 | Product B | 4.00 | 5-6-2020
One method is a correlated subquery:
select t.*
from t
where t.effective_date = (select max(t2.effective_date)
from t t2
where t2.id = t.id and
t2.effective_date <= '2020-07-06'
);

Turn results of count distinct into something that can be aggregated

I have a table like this:
+----------+--------------+-------------+
| category | sub_category | customer_id |
+----------+--------------+-------------+
| A | AB2 | A876 |
| A | AB2 | A876 |
| A | AA1 | A876 |
| A | AA1 | A876 |
| A | AC3 | A756 |
| B | AB2 | A876 |
| B | AA1 | A756 |
| B | AB7 | A908 |
| C | AA1 | A756 |
| C | AB7 | A908 |
| C | AC3 | A908 |
+----------+--------------+-------------+
And I want to count distinct customers so I can easily do something like:
SELECT category, sub_category, COUNT(DISTINCT customer_id) as count_of_customers
FROM tbl
GROUP BY category, sub_category
And I get a report that gives me distinct customers for each sub_category and category. But these numbers can no longer be aggregated as there needs to be de-duplication if I just need distinct customers by category only.
For e.g customer_id = 'A876' will be counted twice in category='A' (once in sub_category = 'AB2' and once in sub_category = 'AA1') if I just sum the count_of_customers from my query result.
So here is the question, I would like to make these query results "aggregatable". Looking at the problem, it looks like this just isn't possible but I am wondering if there some clever way of distributing these results across categories? so that in my reporting layer (like an excel pivot table), I can get a result that counts 'A876' once in category='A' but counts it twice when I also include sub_category in the fields. Basically converting the results into something summable.
I should mention that this is an overly simplified example. The solution will need to generalize across n different categories and sub_categories.
I am looking for an output that would easily allow me to get either of the following results in something similar to a pivot table (think tableau-like reporting tools):
+----------+--------------------+
| category | distinct_customers |
+----------+--------------------+
| A | 2 |
| B | 3 |
| C | 2 |
+----------+--------------------+
+--------------+--------------------+
| sub_category | distinct_customers |
+--------------+--------------------+
| AA1 | 2 |
| AB2 | 1 |
| AB7 | 1 |
| AC3 | 2 |
+--------------+--------------------+
My immediate thought is to assign weights to a customer_id depending on how many categories and sub_categories it occurs in but I don't know exactly how I'd go about doing this.
You can do exactly what you want -- assigning weights. But this still won't aggregate correctly. Assuming there are no duplicates:
select category, sub_category,
count(distinct customer_id),
sum(1.0 / num_cs) as weighted_customers
from (select t.*,
count(*) over (partition by customer_id) as num_cs
from t
) t
group by category, sub_category;
This weights by both category and sub_category. Obviously, you can adjust the partition by to weight by just one or the other.

SELECT effective price based on dates

I am currently working on a supermarket database design where I have to have retrieve the effective price of the product based on the dates.
For example, price of Product A is $9.50 and it will be $10 effective from 1 July. However, this month is still June, so the price of Product A will still be the original price which is $9.50
The problem that I am facing is I am not able to select only one record from each product but all of them.
I am currently using SQL Server.
Price Table:
product_id | product_name | price | effective_date
=======================================================
1 | Product A | 8.00 | 1-5-2020
1 | Product A | 9.50 | 1-6-2020
1 | Product A | 10.00 | 1-7-2020
2 | Product B | 4.00 | 5-6-2020
2 | Product B | 4.50 | 5-7-2020
Codes:
SELECT Product.id, Product.product_name,Price_Table.price, Price_Table.effective_date,
(select top 1 price from Price_Table
where Price_Table.product_id=Product.id and getdate()>= effective_date
order by effective_date desc) 'Latest Price'
FROM Price_Table, Product
WHERE
Product.id=Price_Table.product_id
Result:
id | product_name | price | effective_date | Latest Price
----------------------------------------------------------------
1 | Product A | 8.00 | 1-5-2020 | 9.50
1 | Product A | 9.50 | 1-6-2020 | 9.50
1 | Product A | 10.00 | 1-7-2020 | 9.50
2 | Product B | 4.00 | 1-6-2020 | 4.00
2 | Product B | 4.50 | 1-7-2020 | 4.00
Expected Result:
id | product_name | price | effective_date | Latest Price
----------------------------------------------------------------
1 | Product A | 9.50 | 1-6-2020 | 9.50
2 | Product B | 4.00 | 1-6-2020 | 4.00
Based on the result, it shows all the records which is wrong. Expected result is the one result I hope to
get.
Can anyone help me with this? Thanks!
You can use cross apply:
SELECT p.*, pt.*
FROM Product p OUTER APPLY
(SELECT TOP (1) pt.*
FROM Price_Table pt
WHERE p.id = pt.product_id AND pt.effective_date <= GETDATE()
ORDER BY pt.effective_date DESC
) pt;

Partial join of two rows

I have the following dataset
id | date | metadata | details
------------------------------------------
1 | 2020-01-01 | fruit | null
1 | null | null | banana, apple
2 | 2020-02-02 | dairy | null
2 | null | null | milk, yogurt
I want to "merge" these two rows so that it looks like this
id | date | metadata | details
------------------------------------------
1 | 2020-01-01 | fruit | banana, apple
2 | 2020-02-02 | dairy | milk, yogurt
I would like to move the row with a non null details column into the null one. Could I get some guidance on how I could get to this? Thank you.
You can aggregate using max(). As aggregate functions ignore NULL values, it will get you the result you want.
select id,
max(date) as date,
max(metadata) as metadata,
max(details) as details
from the_table
group by id;

GET DATA FROM TXT SUM GROUP BY AND SUBTRACT

Trying to get data from txt file which contains buys and sells like in the format below.
I want to group by items and subtract each other.
I made three queries like totalin and totalout and stock but when I subtract out from in, some items are missing which has not been sold.
This is the data table
+------+---------+--------+
| TYPE | ITEM | AMOUNT |
+------+---------+--------+
| BUY | APPLE | 100 |
| BUY | ORANGE | 100 |
| BUY | APPLE | 200 |
| BUY | ORANGE | 200 |
| SELL | APPLE | 50 |
| SELL | APPLE | 50 |
| SELL | ORANGE | 100 |
| SELL | ORANGE | 100 |
| BUY | COCONUT | 50 |
| SELL | BANANE | 30 |
+------+---------+--------+
I want this output
+---------+--------+
| ITEM | AMOUNT |
+---------+--------+
| APPLE | 200 |
| BANANE | -30 |
| COCONUT | 50 |
| ORANGE | 100 |
+---------+--------+
I made 3 queries for the result that i want, but unfortunately I am stuck.
Here is my queries
QUERY 1 TOTAL IN:
SELECT DATA.TYPE, DATA.ITEM, Sum(DATA.AMOUNT) AS TOTALIN
FROM DATA
GROUP BY DATA.TYPE, DATA.ITEM
HAVING (((DATA.TYPE)="BUY"));
QUERY 2 TOTAL OUT:
SELECT DATA.TYPE, DATA.ITEM, Sum(DATA.AMOUNT) AS TOTALOUT
FROM DATA
GROUP BY DATA.TYPE, DATA.ITEM
HAVING (((DATA.TYPE)="SELL"));
QUERY 3 STOCK:
SELECT DATA.ITEM, [BUY]![TOTAL_IN]-[SELL]![TOTAL_OUT] AS STOK
FROM (DATA INNER JOIN BUY ON DATA.ITEM = BUY.ITEM) INNER JOIN SELL ON DATA.ITEM = SELL.ITEM
GROUP BY DATA.ITEM, [BUY]![TOTAL_IN]-[SELL]![TOTAL_OUT];
How can I made a query which shows the rest as stock.
Many thanks.
You can sum in one go like:
SELECT ITEM,
Sum(iif(data.type = "BUY", AMOUNT, -AMOUNT)) AS Stock
FROM DATA
GROUP BY ITEM;
You can think of SELL as -ve and BUY as +ve Amount.
Then the query becomes as follows
select item
,sum(case when type='BUY' then Amount
when type='SELL' then -Amount
end) as Amount
from data_table
group by item
In MsAccess, you can apply an if-logic block using the iif function and run the query in one step as below.
SELECT item,
SUM(iif ([type] = "SELL", (-1 * Amount), Amount)) as amounts
FROM data
GROUP BY item