Group by sum with date and two different tables - sql

The following extracts of two tables are given (Oracle SQL):
+----------+------------+-------------+
| Orders | | |
+----------+------------+-------------+
| Order ID | Date | Customer ID |
| 12345 | 12.05.2018 | 456 |
| 12346 | 01.09.2021 | 646 |
| 12347 | 03.03.2019 | 836 |
| 12348 | 04.06.2020 | 1026 |
| 12349 | 05.07.2020 | 1216 |
| 12350 | 04.01.2020 | 1406 |
+----------+------------+-------------+
+-------------+----------+
| Country | |
+-------------+----------+
| Customer ID | Country |
| 1026 | GB |
| 836 | USA |
| 1026 | Germany |
| 2166 | USA |
| 2546 | GB |
| 4154 | France |
+-------------+----------+
The desired outcome should provide lines with Order ID, Date, Customer ID, Country as well as:
The amount of orders by the customer of a certain order ID over the last 10 and 30 days
The amount of orders by the country of a certain customer over the last 10 and 30 days
Since every customer belongs to a country the aggregated amounty by country are always at least as high as by a customer.
So the result should look like this:
+--------------------------------------+------------+-------------+------------+-------------------------------------+-------------------------------------+------------------------------------+------------------------------------+
| Desired Outcome (Results fictitious) | | | | | | | |
+--------------------------------------+------------+-------------+------------+-------------------------------------+-------------------------------------+------------------------------------+------------------------------------+
| Order ID | Date | Customer ID | Country ID | Amount Orders Cutsomer Last 10 Days | Amount Orders Cutsomer Last 30 Days | Amount Orders Country Last 10 Days | Amount Orders Country Last 30 Days |
| 12347 | 03.03.2019 | 836 | USA | 7 | 15 | 124 | 578 |
+--------------------------------------+------------+-------------+------------+-------------------------------------+-------------------------------------+------------------------------------+------------------------------------+

Your requirements are not clearly stated but I think you are asking for a list of every order within last 30 days and a summary count by Customer and Country for within 10 and within 30 days.
In a CTE/SubQuery
Join the two table together using customer ID
add logical columns for order within 10 days, order within 30 days. These should return true = 1, false = 0
Select from CTE,
use
SUM(Within10) OVER(PARTITION BY CustomerID),
SUM(Within30) OVER(PARTITION BY CountryCode),
SUM(Within10) OVER(PARTITION BY CustomerID),
SUM(Within30) OVER(PARTITION BY CountryCode),
This is T-SQL so the syntax for PL/SQL may vary slightly

Related

[SQL ]How to sum the value in some parts of column

I am practicing SQL and here is my exercise(Table 1)
Table 1: The origin table
My goal is to have the sum of income when Month is between 1 and 3. If Month=4, the income senter code herehould not be added in.
|M_Id | Year | Month | CompanyID | CustomerID | MonthIncome |
|1 | 110 | 1 | T012 | C001 | 30000 |
|2 | 110 | 2 | T012 | C001 | 60000 |
|3 | 110 | 3 | T012 | C001 | 60000 |
|4 | 110 | 4 | T012 | C001 | 100000 |
|5 | 110 | 1 | A012 | A001 | 10000 |
|6 | 110 | 1 | A012 | A001 | 50000 |
I tried some SQL:
select companyID, customerID, Year, Sum(MonthIncome) as Total
from[dbo].[Money]
group by year,companyID, customerID
and the table result look like this:
Table 2. using sum, group by , and the table become
| Year | CompanyID | CustomerID | MonthIncome|
| 110 | A012 | A001 | 60000|
| 110 | T012 | C001 | 250000|
The table style is what I want, but the sum(Income) is not right because it had included month=4.
I tried to change my sql to
select companyID, customerID, Year, Sum(MonthIncome) as Total
from[dbo].[Money]
group by year,companyID, customerID
having month between 1 and 3
but the system as me to put the month into group by, and then table style is not what I want.
Could anybody help me?
You can exclude the months that are not in the range 1 to 3 using WHERE then do the grouping:
SELECT year, companyID, customerID, Sum(MonthIncome) AS Total
FROM [dbo].[Money]
WHERE month BETWEEN 1 AND 3
GROUP BY year, companyID, customerID;

How to get greatest Date record and multiply to that dynamically

Hello Everyone I want to get greatest Date record and multiply to that dynamically below is sample structure of my table and here is DB fiddle https://dbfiddle.uk/?rdbms=oracle_18&fiddle=cde3fdc07915a2e8c23195be646c5a20
+-----+-------------+-----------+--------+----------------+
| ID | Sequence Id | Date | Amount | Frequency |
+-----+-------------+-----------+--------+----------------+
| 123 | 1 | 01-Jan-20 | 50 | Monthly |
| 123 | 2 | 01-Feb-20 | 50 | Monthly |
| 123 | 3 | 01-Mar-20 | 150 | Monthly |
| 123 | 4 | 01-Apr-20 | 200 | Monthly |
| 123 | 5 | 01-May-20 | 510 | Monthly |
| 123 | 1 | 01-Jan-20 | 510 | Quarterly |
| 123 | 2 | 01-Apr-20 | 300 | Quarterly |
| 123 | 1 | 01-Jan-20 | 600 | Semi-Annually |
+-----+-------------+-----------+--------+----------------+
I want to retrieve data dynamically with the help of filter and want to multiply amount according to Frequency. Get greatest record according to Date and multiply amount with 12 if frequency monthly or multiply 4 if frequency Quarterly or multiply 2 if frequency Semi-Annually
Ex. 1. If we run query select ID, Rent from Table where Date is greater than or equal 01-jan-2020 and less than or equal to 01-may-2020 and frequency equal to Monthly then out put should be like below -
+-----+-------------+
| ID | Rent |
+-----+-------------+
| 123 | 6,120 |
+-----+-------------+
2. If we run query select ID,Rent from Table where Date is greater than or equal 01-jan-2020 and less than or equal to 01-may-2020 and frequency equal to Quarterly then out put should be like below -
+-----+-------------+
| ID | Rent |
+-----+-------------+
| 123 | 1200 |
+-----+-------------+
3. If we run query select ID,Rent from Table where Date is greater than or equal 01-jan-2020 and less than or equal to 01-may-2020 and frequency equal to Semi-Annually then out put should be like below -
+-----+-------------+
| ID | Rent |
+-----+-------------+
| 123 | 1200 |
+-----+-------------+
If you want this over multiple ids and frequencies at once, then you can use row_number() like so:
select id,
amount * case frequency
when 'Monthly' then 12
when 'Quaterly' then 4
when 'Semi-Annually' then 2
end as rent
from (
select t.*, row_number() over(partition by id, frequency order by StartDate desc) rn
from table1 t
where StartDate between date '2020-01-01' and date '2020-05-01' and frequency = 'Monthly'
) t
where rn = 1

SQL - Join on changing dates

I've been scratching my head on this for a week now.
Consider two tables - one tallying inventory:
+------------+--------------+----------+-------------------+
| product_id | product_name | date | on_hand_inventory |
+------------+--------------+----------+-------------------+
| 1 | Product A | 6/1/2019 | 37 |
| 1 | Product A | 6/2/2019 | 36 |
| 1 | Product A | 6/3/2019 | 35 |
| 1 | Product A | 6/4/2019 | 40 |
| 1 | Product A | 6/5/2019 | 42 |
+------------+--------------+----------+-------------------+
... and another tracking costs:
+------------+----------------+------------+------------+------------+
| product_id | cost_component | cost_value | start_date | end_date |
+------------+----------------+------------+------------+------------+
| 1 | FOB | 15 | 1/1/2019 | 6/1/2019 |
| 1 | FOB | 15.5 | 6/2/2019 | 6/3/2019 |
| 1 | FOB | 16 | 6/4/2019 | 12/31/9999 |
+------------+----------------+------------+------------+------------+
The layout of the cost table is what's driving me nuts. I need to join these
tables to keep a running valuation of on-hand inventory, and I can't think of a
method in SQL that would let me select the appropriate row in the cost table.
A join on produt_id doesn't work because it would return all cost components for
that item whether or not they apply to that date. I feel like should be
involving a CASE statement, but I'm not sure what that would look like.
This is in MSSQL 2016, for what its worth.
If you want the most recent cost, you can use join:
select t.*, c.*
from inventory i join
costs c
on c.product_id = i.product_id and
i.date between c.start_date and c.end_date;

Calculate percentage of revenue per month

I have the following underlying data:
+-------+-------+---------------+
| Order | Month | sqft produced |
+-------+-------+---------------+
| 1001 | 4 | 10.29 |
| 1001 | 6 | 4'367.66 |
| 1001 | 7 | 203.57 |
| 1001 | 8 | 294.61 |
| 1001 | 9 | 92.28 |
| 1001 | 10 | 34.47 |
| 1001 | 12 | 16.59 |
| 1002 | 1 | 1.74 |
| 1002 | 4 | 19.54 |
| 1002 | 7 | 5'552.21 |
| 1002 | 9 | 309.62 |
| 1002 | 10 | 24.15 |
| 1002 | 12 | 52.16 |
| 1003 | 5 | 807.45 |
+-------+-------+---------------+
Those are three orders and I want to split the revenue according to the percentage of sqft produced in each month.
The revenue table:
+-------+-----------+
| Order | Revenue |
+-------+-----------+
| 1001 | 1'135'465 |
| 1002 | 1'773'499 |
| 1003 | 172'633 |
+-------+-----------+
So the output of the query should look like this:
+-------+-------+------------------+
| Order | Month | Revenue produced |
+-------+-------+------------------+
| 1001 | 4 | 2'327.72 |
| 1001 | 6 | 988'017.67 |
| 1001 | 7 | 46'050.00 |
| 1001 | 8 | 66'644.36 |
| 1001 | 9 | 20'874.86 |
| 1001 | 10 | 7'797.53 |
| 1001 | 12 | 3'752.86 |
| 1002 | 1 | 517.82 |
| 1002 | 4 | 5'815.02 |
| 1002 | 7 | 1'652'314.97 |
| 1002 | 9 | 92'141.64 |
| 1002 | 10 | 7'186.94 |
| 1002 | 12 | 15'522.60 |
| 1003 | 5 | 172'633.00 |
+-------+-------+------------------+
I am struggling with a way of getting the underlying data in that format because I can't seem to be able to split it by month.
Getting the percentage via dividing the square footage over the sum of the square footage partitioned over the [Order] would get you what you are looking for:
select p.[Order],
[Month],
Revenue,
[sqft produced],
CAST([sqft produced] / SUM([sqft produced]) OVER(PARTITION BY p.[Order]) AS DECIMAL(10,5)) * 100 as sqft_percentage,
Revenue * CAST([sqft produced] / SUM([sqft produced]) OVER(PARTITION BY p.[Order]) AS DECIMAL(10,5)) as revenue_produced
FROM [Orders] p
INNER JOIN Revenue r
ON r.[Order] = p.[Order]
Let's call the first table sqft and the second revenues. To avoid reserved words, the first table column's names will be:
order_id, month_num, sqft_produced
The second:
order_id, total_revenue
The SQL for what you need goes something like this (this is for Oracle):
SELECT order_id, month_num, (sqft_produced / total_sqft * total_revenue) revenue_produced
FROM sqft INNER JOIN
(SELECT order_id, total_sqft, total_revenue FROM (
(SELECT order_id, sum(sqft_produced) total_sqft
GROUP BY order_id) sqfts INNER JOIN revenues ON sqfts.order_id = revenues.order_id)) totals
ON sqft.order_id = totals.order_id;
Step by step:
SELECT order_id, sum(sqft_produced) total_sqft
GROUP BY order_id;
This gets you total square feet produced by order.
(SELECT order_id, sum(sqft_produced) total_sqft
GROUP BY order_id) sqfts INNER JOIN revenues ON sqfts.order_id = revenues.order_id;
This gets you a table with total revenues and total square feet.
And then you join this table to your sqft table and divide the square feet produced each month by total square feet and multiply by total revenue, thus splitting the revenue proportionally to square feet.

postgresql aggregate of aggregate (sum of sum)

I've got workers who have many sales and who belong to departments. I'd like to see how many sales a department is making per day.
For simplicity, let's say a worker belongs to only one department.
Example:
departments:
| id | name |
| 1 | Men's Fashion |
| 2 | Women's Fashion |
workers:
| id | name |
| 1 | Timmy |
| 2 | Sally |
| 3 | Johnny |
sales:
| id | worker_id | datetime | amount |
| 1 | 1 | 2013-1-1 08:00:00 | 1 |
| 2 | 1 | 2013-1-1 09:00:00 | 3 |
| 3 | 3 | 2013-1-2 08:00:00 | 8 |
department_employees
| id | worker_id | department_id |
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 2 |
I'd like to get
| department | amount |
| Men's Fashion | 4 |
| Women's Fashion | 8 |
To get the individual worker's total sales, I can do
SELECT worker_id, SUM(amount) FROM sales
GROUP BY worker_id
How do I take those sums (the total amount sold per worker) and aggregate it by department?
Don't sum the sum, rather join from sales through the department_employees table to the department:
select d.name, sum(s.amount)
from sales s
join department_employees de on de.worker_id = s.worker_id
join departments d on d.id = de.department_id
group by d.name
Aggregate functions and group by work in a statement with joints too.
Try something like:
SELECT name, SUM(amount) FROM departments, department_employees, sales
WHERE departments.id = department_employees.department_id
AND sales.worker_id = department_employees.worker_id
GROUP BY name