how to display the total sum of all individual dates in Oracle - sql

I have a query that should display the total sum of sales of all individual dates,not the separate sales in each day. Below is the query I have tried and I am attaching a sample image of the output that I have gotten from this query. Your help would be appreciated.
SELECT sc_cd,Mon,sum(NET_SAL) SALE
FROM (SELECT TO_CHAR(to_date(deli_DT),'Mm') mm,
sc_cd,
TO_CHAR(to_date(deli_DT),'dd-Mon-yy') Mon,
sum(sale_net) NET_SAL
from bill_mas
where sc_cd not in ('22')
AND deli_dt BETWEEN '01-aug-15' and '31-aug-15'
AND CANCL IS NULL
AND sc_cd='01'
GROUP BY TO_CHAR(to_date(deli_DT),'Mm'),
SC_cd,
TO_CHAR(to_date(deli_DT),'dd-Mon-yy'),
sale_net)
ORDER BY 3;

You have the sale_net column in the group_by clause, so you will still see one row for each value - you'll only see any actual aggregation if you have two source rows with the same value. Remove that from the group by. It's also not clear why you're using a subquery; if you don't want MM in the output, just don't select it in the first place:
SELECT sc_cd,TO_CHAR(deli_DT,'dd-Mon-yy') Mon,sum(sale_net) NET_SAL
from bill_mas
where sc_cd not in('22')
and deli_dt BETWEEN '01-aug-15' and '31-aug-15'
and CANCL IS NULL
AND sc_cd='01'
GROUP BY SC_cd,TO_CHAR(deli_DT,'dd-Mon-yy')
order by 3
You should perhaps be selecting and grouping by trunc(deli_DT) rather than TO_CHAR(to_date(deli_DT),'dd-Mon-yy'), but if you need to format it anyway then it might not matter. But if deli_DT is a date field - as it seems to be, though it isn't entirely clear - then you should not be doing to_date() on it at all, as Boneist commented. You're really doing to_date(to_char(deli_dt)), with two implicit conversions using your NLS_DATE_FORMAT.
Using strings for your filter isn't a good idea though, and neither is using two-digit years; and you won't be seeing any rows which are from 2015-08-31 but after midnight; you should use explicit date conversions or literals, and use greater than/less than instead of between:
and deli_dt >= to_date('01-aug-2015', 'DD-mon-YYYY')
and deli_dt < to_date('01-sep-2015', 'DD-mon-YYYY'
Or:
and deli_dt >= date '2015-08-01'
and deli_dt < date '2015-09-01'

The issue is simple, you must eliminate sale_net column from group by clause.
Additionaly, if deli_DT is a Date datatype, you should write the query without to_date function. Also, you don't need two group by clauses:
SELECT
TO_CHAR(deli_DT,'Mm') mm,
sc_cd,
TO_CHAR(deli_DT,'dd-Mon-yy') Mon,
sum(sale_net) NET_SAL
from bill_mas
where sc_cd not in('22')
and deli_dt BETWEEN '01-aug-15' and '31-aug-15'
and CANCL IS NULL
AND sc_cd='01'
GROUP BY TO_CHAR(deli_DT,'Mm'), sc_cd, TO_CHAR(deli_DT,'dd-Mon-yy')
order by 3;

If you are using oracle and want sales by day you can use analytic function like-
sum(net_sale) over(partition by mon) from your_table;
it will give you sale of each day like I have mad e a temp1 table like this-
id mon net_sale
1 05-08-15 123
1 05-08-15 23
1 05-08-15 1
1 12-08-15 23
1 12-08-15 455
1 12-08-15 122
and the output is like-
Mon net_sale
05-08-15 147
05-08-15 147
05-08-15 147
12-08-15 600
12-08-15 600
12-08-15 600

Related

SQL Snowflake Min Date For Each Record & Group By Month

I am using Snowflake to get some information from the data. I have a table in which the same employee (PRS_ID) can be included in many rows and I want to take the EARLIEST date (BGN_DATE) for each employee (PRS_ID). Next, I want to see how many employees I have each month.
Ideally if I could have in YYYY-MM format (YYYYMM will work too). What I was working with is below:
SELECT PRS_ID, MIN(BGN_DATE), MONTH(BGN_DATE)
FROM myTable
WHERE EMP_STS = 'T'
GROUP BY PRS_ID, MONTH(BGN_DATE)
However, this will show me the data per employee (PRS_ID), which is too granular (as explained above). But when I remove "PRS_ID" from grouping I get below error:
SQL compilation error: error line 1 at position 7 'myTable.PRS_ID' in select clause is neither an aggregate nor in the group by clause.
Does anyone know how to fix it?
Thank you
Sample Data:
PRS_ID
EMP_STS
BGN_DATE
homsimps
T
2022-01-30
homsimps
T
2022-02-28
homsimps
T
2022-03-30
bartsimps
T
2022-01-30
bartsimps
T
2022-02-28
bartsimps
T
2022-03-31
lisasimps
T
2022-04-30
lisasimps
T
2022-05-31
lisasimps
T
2022-06-30
lisasimps
T
2022-07-30
margesimps
T
2022-02-28
margesimps
T
2022-03-30
Expected Outcome:
Period
Count
2022-01
2
2022-02
1
2022-03
0
2022-04
1
Using aggregation twice:
WITH cte AS (
SELECT PRR_ID, MIN(BGN_DATE) AS min_bgn_date
FROM my_table
WHERE EMP_STS = 'T'
GROUP BY PRS_ID
)
SELECT TO_VARCHAR(min_bgn_date, 'YYYYMM') AS month, COUNT(*) AS cnt
FROM cte
GROUP BY TO_VARCHAR(min_bgn_date, 'YYYYMM');
-- GROUP BY month
There is a simpler function here, DATE_TRUNC will allow you to convert dates to months. You can then convert to the format you'd like.
WITH MY_CTE AS (
SELECT
PRS_ID,
DATE_TRUNC(MONTH, MIN(BGN_DATE)) AS MONTH_START
FROM
myTable
WHERE
EMP_STS = 'T'
GROUP BY 1
)
SELECT
TO_CHAR(MONTH_START, 'yyyy-mm') AS PERIOD,
COUNT(PRS_ID)
GROUP BY 1

Return ID if sum of value is between two possible date ranges, and output which date range it came from?

I have a table that has a similar structure as this:
UserID
Amount
Date
123
50
01/01/2021
234
105
02/01/2021
123
60
01/15/2021
345
70
01/15/2021
456
110
12/31/2020
345
50
02/15/2020
I have two date ranges, 01/01/2021 - 01/31/2021 and 02/01/2021 - 02/28/2021. I want to get a list of UserIDs if they had total amount >= 100 within these date ranges, and to specify which date range it came from.
So in this example, I'd like an output like this:
UserID
Total Amount
Date Range
123
110
January
234
105
February
User ID 345 and 456 would not be included since their total amounts only reached >= 100 outside of the date ranges.
In my code, I'm not sure how to exclude UserID 345 since technically they have an amount >= 100 coming from dates in both ranges and not just one single range.
I'm having troubles with only summing if it's within the date range and I'm not sure how to specify from which date range it's coming from:
SELECT
UserID
,SUM(amount)
FROM table
WHERE
date BETWEEN '01-01-2021' AND '01-31-2021' OR
date BETWEEN '02-01-2021' AND '02-28-2021'
GROUP BY
UserID
HAVING SUM(amount) >= 100
You could use CROSS APPLY to calculate the date range, and then group by it.
But in this instance, it appears you are just using whole months, so we can just use DATENAME.
Side notes:
You should not use mm-dd-yyyy format for date literals
You can combine your two periods together in this instance, I have not done so to leave you the option of disjoint periods.
SELECT t.UserID,
SUM(t.amount),
Month = DATENAME(month, t.date)
FROM table t
WHERE t.Date BETWEEN '2021-01-01' AND '2021-01-31' OR
t.date BETWEEN '2021-02-01' AND '2021-02-28'
GROUP BY UserId, DATENAME(month, t.date);
NOTE: if your dates are actually datetime values then your filter is wrong. Instead you must use a half open interval:
WHERE t.Date >= '2021-01-01' AND t.Date < '2021-02-01' OR
t.date >= '2021-02-01' AND t.Date < '2021-03-01'
If your date ranges are months, you can join to the ranges and then aggregate by user:
select min(range_name), user_name, sum(amount)
from t join
(values ('2021-01-01', '2021-01-31', 'January'),
('2021-02-01', '2021-02-28', 'February')
) v(range_start, range_end, range_name)
on t.date >= v.range_start and t.date <= v.range_end
group by userid
having count(distinct range_name) = 1 and
sum(amount) > 100;
The having clause limits the results both by the number of ranges and by the amount.
If you just wants months between particular dates, I would simplify this to:
select datepart(month, min(date)), userid, sum(amount)
from t
where date >= '2021-01-01' and date < '2021-03-01'
group by userid
having count(distinct month(date)) = 1 and
sum(amount) > 100;

SQL SUM OVER PARTITION BY 2 columns not working

Common question, I know. Just haven't been able to find a solution to my question, so hit me with the removal and or downvotes if you must.
(Oracle 12c)
I have data that looks like this:
Date ITEM QTY
01-MAR-20 STS 6920
01-MAR-20 STS 2581
01-MAR-20 S01 22606
01-MAR-20 S01 22312
01-MAR-20 S01 56000
....
I want to get QTY to aggregate (sum) at the Date and item level with only one record for each unique item on each date, so it looks like this:
Date ITEM QTY
01-MAR-20 STS 9501
01-MAR-20 S01 100918
The query I'm trying to use to do this is:
SELECT
WO.DATE,
D.ITEM,
SUM(WO.QUANTITY) OVER (PARTITION BY WO.DATE, D.ITEM) AS QTY
FROM SCHEMA_1.WO,
SCHEMA_2.D
WHERE WO.ITEM_DIM_KEY = D.ITEM_DIM_KEY AND
(DATE > '01 MAR 2020' AND DATE < '01 JUL 2020')
ORDER BY WO.COMPLETED_DATE;
A date data type in Oracle can have a time component. So you need to be careful. Unless you know hat you have no time component, trunc() is safer. Also, you can use the date keyword to handle date constants:
SELECT
SELECT TRUNC(WO.DATE), D.ITEM,
SUM(WO.QUANTITY)
FROM SCHEMA_1.WO JOIN
SCHEMA_2.D
ON WO.ITEM_DIM_KEY = D.ITEM_DIM_KEY
WHERE WO.DATE >= DATE '2020-03-01' AND
WO.DATE < DATE '2020-07-01'
GROUP BY TRUNC(WO.DATE), D.ITEM
ORDER BY TRUNC(WO.DATE);
Notes:
You don't need an analytic function. Aggregation should be sufficient.
Use proper, explicit, standard, readable JOIN syntax.
I assume the column in the ORDER BY is intended to be the first column in the result set.
You just need a basic GROUP BY query:
SELECT
WO.DATE,
D.ITEM,
SUM(WO.QUANTITY) AS QTY
FROM SCHEMA_1.WO
INNER JOIN SCHEMA_2.D
ON WO.ITEM_DIM_KEY = D.ITEM_DIM_KEY
WHERE
DATE > '01 MAR 2020' AND DATE < '01 JUL 2020'
GROUP BY
WO.DATE,
D.ITEM
ORDER BY
WO.COMPLETED_DATE;
Using SUM as a window function would make sense if you wanted to retain every record in the result set of the join. However, in your case, you want to report aggregate sums for each date/item group. Using GROUP BY is what you want here.
I think you just need a simple aggregation:
select trunc(date), item, sum(qty)
from schema_1.wo
join schema_2.d on wo.item_dim_key = d.item_dim_key
where date > date '2020-03-01' and date < date '2020-07-01'
group by trunc(date), item
order by wo.completed_date
Incidentally, I upgraded the JOIN to SQL-92 and fixed the dates literal to ISO dates instead of VARCHARs.

Apply date value in group by but SELECT statement output field getting DateTime value

Table Structure:
ID DeviceNumber CreatedDate
1 101 2018-07-07 07:50:19.0000000
2 101 2018-07-07 01:50:19.0000000
3 101 2018-07-07 06:50:19.0000000
4 101 2018-07-08 02:50:19.0000000
Output:
ID DeviceNumber CreatedDate
1 101 2018-07-07 07:50:19.0000000
2 101 2018-07-08 02:50:19.0000000
Here i'm applying GROUPBY clause in CreatedDate field thats why i seperated Day-Month-Year, but in query output i need Date and Time in CreatedDate field value.
My SQL Statement:
SELECT DeviceNumber,
Convert(varchar(30),Concat(CalMonth, '-' , CalDay, '-' ,CalYear),102) as CreateDateTime
FROM
(
select DeviceNumber, DAY(CreatedDate) as CalDay, MONTH(CreatedDate) as CalMonth, YEAR(CreatedDate) as CalYear
from TableDevice
) AS T
GROUP BY DeviceNumber, CalDay, CalMonth, CalYear
In output i need DeviceNumber and CreatedDate (with Date and Time)
Thanks in an advancd
It seems that you used the GROUP BY in order to merge all the rows with the same day in one, but you still need a time. Which time, though? You need an aggregation function that will return the appropriate one. Since it's called CreatedDate, I bet it's the first one you need (min).
select DeviceNumber, min(CreatedDate)
from TableDevice
group by DeviceNumber, convert(date,CreatedDate)
The convert(date,CreatedDate) is an easier way to group by excluding the time part: The conversion will only keep the date and not the time.
you need max() with group by date
select DeviceNumber, max(CreatedDate) as CreatedDate
from TableDevice
group by DeviceNumber, convert(date,CreatedDate)
If needs all fields in selection then you can use corelated subquery
select t1.* from TableDevice t1
where CreatedDate= (select max(CreatedDate)
from TableDevice t2
where convert(date,t2.CreatedDate)=convert(date,t1.CreatedDate)
)
As previous responses stated you need to specify what time do you want to associate with aggregated date?
But if you wanted to have date and time in separate fields use DATE(CreatedDate) and TIME(CreatedDate)

How can I pivot my T-SQL query output to get this specific table?

I am running a T-SQL query on SQL Server 2014. The query and its output are given below:
Use MyDatabase
SELECT
ID,
ArrivalMonth,
DateOfBirth
FROM [View1]
WHERE [ArrivalMonth] between '2017-01-01' and '2018-05-01'
The output of the above query looks like this (extract):
ID ArrivalMonth DateOfBirth
101 2017-01-01 1974-05-30
105 2017-05-01 1967-03-05
125 2017-05-01 NULL
... ... ...
I need a T-SQL query to give me the following output (based on the output above):
ArrivalMonth Number_Of_Bookings Number_Of_DOB_Captured
2017-01-01 130 110
2017-02-01 90 85
... ... ...
2018-05-01 115 70
The first column is the ArrivalMonth. Number_Of_Bookings is the count of number of records from the above query. Number_Of_DOB_Captured is the count of DateOfBirth which is NOT NULL.
I think may be the Pivot query might be the solution but I am confused as to how to execute it in this scenario.
You may left join a calendar table containing all the months to your current table, and then aggregate:
WITH months AS (
SELECT '2017-01-01' AS month UNION ALL
SELECT '2017-02-01' UNION ALL
...
SELECT '2017-12-01'
)
SELECT
m.month,
COUNT(*) AS Number_Of_Bookings,
COUNT(v.DateOfBirth) AS Number_Of_DOB_Captured
FROM months m
LEFT JOIN [View1] v
ON m.month = v.ArrivalMonth
WHERE
v.ArrivalMonth BETWEEN '2017-01-01' AND '2018-05-01'
GROUP BY
m.month;
The calendar table may be necessary here if it could be possible that, for some reason, a given arrival month have no data associated with it in your view. If you are certain that the view would always contain data for every month, then you may aggregate directly on your table without joining.
you can use count(Number_Of_Bookings), count(DateOfBirth) and group by ArrivalMonth
So you count the number of non null values for each different ArrivalMonth.
the query :
Select ArrivalMonth
, count(Number_Of_Bookings)
, count(DateOfBirth)
FROM [View1]
WHERE [ArrivalMonth] between '2017-01-01' and '2018-05-01'
group by ArrivalMonth