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

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;

Related

Group by sum with date and two different tables

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

SQL - Calculate number of occurrences of previous day?

I want to calculate the number of people who also had occurrence the previous day on a daily basis, but I'm not sure how to do this?
Sample Table:
| ID | Date |
+----+-----------+
| 1 | 1/10/2020 |
| 1 | 1/11/2020 |
| 2 | 2/20/2020 |
| 3 | 2/20/2020 |
| 3 | 2/21/2020 |
| 4 | 2/23/2020 |
| 4 | 2/24/2020 |
| 5 | 2/22/2020 |
| 5 | 2/23/2020 |
| 5 | 2/24/2020 |
+----+-----------+
Desired Output:
| Date | Count |
+-----------+-------+
| 1/11/2020 | 1 |
| 2/21/2020 | 1 |
| 2/23/2020 | 1 |
| 2/24/2020 | 2 |
+-----------+-------+
Edit: Added desired output. The output count should be unique to the ID, not the number of date occurrences. i.e. an ID 5 can appear on this list 10 times for dates 2/23/2020 and 2/24/2020, but that would count as "1".
Use lag():
select date, count(*)
from (select t.*, lag(date) over (partition by id order by date) as prev_date
from t
) t
where prev_date = dateadd(day, -1, date)
group by date;

Count Distinct Over Multiple Columns

I have two CTEs . The following is the output of my first CTE.
| ORDER_NUMBER | ORDER_FLAG | EMPLOYEE | PRODUCT_CATEGORY | SALES |
|--------------|------------|----------|------------------|--------|
| 3158132 | 1 | Don | Newpaper Ad | 16.00 |
| 3158132 | 1 | Don | Magazine Ad | 15.00 |
| 3158132 | 0 | Don | TV Ad | 0.00 |
| 3158132 | 1 | Don | Billboard Ad | 56.00 |
| 3006152 | 1 | Roger | TV Ad | 20.00 |
| 3006152 | 0 | Roger | Magazine Ad | 0.00 |
| 3006152 | 1 | Roger | Newspaper Ad | 214.00 |
| 3012681 | 1 | Ken | TV Ad | 130.00 |
| 3012681 | 0 | Ken | Magazine Ad | 0.00 |
| 9818123 | 1 | Pete | Billboard Ad | 200.00 |
I'm attempting to count the distinct order numbers and the sales amount by employee. The order flag will be either 1 or a 0. If sales are greater than 0.00 the order flag will be set to 1.
My desired output.
| Employee | Sales | Orders |
|----------|--------|--------|
| Don | 87.00 | 1 |
| Ken | 130.00 | 1 |
| Pete | 200.00 | 1 |
| Roger | 234.00 | 1 |
I was attempting to do a combination of distinct, case, and concat statements without any luck. Any thoughts?
You can use this:
with cteTotalSales (...) as ()
select employee,
case when (sum(sales)) > 0
then 1 else 0 as Orders,
sum(sales)
from cteTotalSales
group by employee
This should be as simple as :
with cte as (...)
select
employee,
sum(sales),
count(distinct order_number)
from cte
group by employee
This query would work for you
SELECT
EMPLOYEE,
SUM(SALES) SALES,
1 AS ORDERS
FROM
YOUR_TABLE
GROUP BY
EMPLOYEE
you can replace your subquery with YOUR_TABLE.
SELECT
EMPLOYEE,
SUM(SALES) SALES,
1 AS ORDERS
FROM
(
SELECT * FROM ...
)
GROUP BY
EMPLOYEE

SELECTing monthly order amounts and item subtotals in a single query with two tables

I have an Orders table in the form:
| id | service_fee_cents | grand_total_cents | created_at |
|----|-------------------|-------------------|---------------|
| 1 | 1400 | 10000 | Jan 21 2018 |
| 2 | 1000 | 10000 | Feb 16 2018 |
| 3 | 500 | 10000 | March 21 2018 |
| 4 | 500 | 10000 | March 20 2018 |
And an Items table in the form
| id | order_id | title | price_cents | quantity |
|----|----------|--------|-------------|----------|
| 1 | 1 | lorem | 2000 | 2 |
| 2 | 1 | ipsum | 2030 | 1 |
| 3 | 2 | pie | 4000 | 4 |
| 4 | 3 | cheese | 6000 | 2 |
| 5 | 3 | burger | 7000 | 1 |
| 6 | 4 | custar | 1000 | 1 |
And I'm trying to run a SQL query to get a result in the form
| month | total_service_fee | total_grand_total | total_subtotal |
|-----------|-------------------|-------------------|----------------|
|2017-11-01 | 42 | 1,610 | 610 |
|2017-12-01 | 30 | 19,912 | 1,912 |
|2018-01-01 | 179 | 1,413 | 413 |
|2018-02-01 | 165 | 2,910 | 910 |
|2018-03-01 | 1,403 | 10,727 | 1,727 |
I've managed to get the first three columns using this query:
SELECT
date_trunc('month', created_at)::date AS month,
SUM(service_fee_cents) / 100 AS total_service_fee,
SUM(grand_total_cents) / 100 AS total_grand_total
FROM orders
GROUP BY month ORDER BY month
How do I get the last one? In the app, I get the sum via the following Ruby code:
order_subtotal = order.items.map{|item| item.price * item.quantity}.reduce(:+)
Which basically takes all the order's items, multiplies price by quantity and adds the results.
This should be a good start:
SELECT Date_trunc('month', created_at) :: DATE AS month,
SUM(service_fee_cents) / 100 AS total_service_fee,
SUM(grand_total_cents) / 100 AS total_grand_total,
SUM(total_subtotal) / 100 AS total_subtotals
FROM orders o
join (SELECT order_id,
SUM(price_cents * quantity) total_subtotal
FROM items i
GROUP BY order_id) i
ON o.id = i.order_id
GROUP BY month
ORDER BY month
You can get there by just joining the Orders table to the Items table and generating a SUM of subtotals by month. This may however be a somewhat expensive query to run if there are thousands of items in each order like you said.
SELECT
date_trunc('month', created_at)::date AS month,
SUM(service_fee_cents) / 100 AS total_service_fee,
SUM(grand_total_cents) / 100 AS total_grand_total,
SUM(price_cents * quantity) / 100 AS sub_total
FROM Orders o
JOIN Items i ON i.order_id = o.id
GROUP BY month ORDER BY month
http://sqlfiddle.com/#!15/555a2/1

How to return smallest value inside the resultset as a separate column in SQL?

I've been struggling with the following SQL query.
My resultset is now:
| Id | Customer | Sales |
| 1 | 1 | 10 |
| 2 | 1 | 20 |
| 3 | 2 | 30 |
| 4 | 2 | 40 |
What I'd like to do is to add additional column that shows the smallest sale for that customer:
| Id | Customer | Sales | SmallestSale |
| 1 | 1 | 10 | 10 |
| 2 | 1 | 20 | 10 |
| 3 | 2 | 30 | 30 |
| 4 | 2 | 40 | 30 |
As the select query to get those three columns is now rather complex I'd like to avoid subqueries.
Any ideas?
Mika
Assuming your RDBMS supports windowed aggregates
SELECT Id,
Customer,
Sales,
MIN(Sales) OVER (PARTITION BY Customer) AS SmallestSale
FROM YourTable
select s.Id, s.Customer, s.Sales, sm.SmallestSale
from Sales s
inner join (
select Customer, min(sales) as SmallestSale
from Sales
group by Customer
) sm on s.Customer = sm.Customer