Related
Using SQL Server 2019, therefore also using T-SQL, I'm trying to get the number of days of a particular week, identified by the ISO 8601 week definition, that fall inside a particular month.
The table that contains the data has the following fields:
Year | Month | IsoWeek
-----------------------
2020 | 12 | 50
2020 | 12 | 51
2020 | 12 | 52
2020 | 12 | 53
2021 | 01 | 01
2021 | 01 | 02
2021 | 01 | 03
For example the last week of 2020, week 53, has started on Monday 28th Dec 2020 and has finished on Sunday 3rd Jan 2021.
So I want to get 4 for Dec2020 and 3 for Jan2021 in order to create a new table like the following:
Year | Month | IsoWeek | WeekDays
------------------------------------
2020 | 12 | 52 | 7
2020 | 12 | 53 | 4
2021 | 01 | 53 | 3
2021 | 01 | 01 | 7
2021 | 01 | 02 | 7
Is there a way to do it entirely in SQL?
This is sort of a SQL Server setting (or trick) which works because the 1st of January, 1900 was a Monday. Since that's where SQL Server starts counting from it makes it easier to locate the first Thursday of any month. Thanks to Jeff Moden btw. I got this from something he wrote. Maybe there's a better way to this now, idk
with iso_dts_cte(yr, mo, wk) as (
select * from (values ('2020', '12', '50'),
('2020', '12', '51'),
('2020', '12', '52'),
('2020', '12', '53'),
('2021', '01', '01'),
('2021', '01', '02'),
('2021', '01', '03')) v(yr, mo, wk))
select iso.*, v.*
from iso_dts_cte iso
cross apply (values (cast(dateadd(wk,datediff(wk,0,'01/04/'+iso.yr),0)+((iso.wk-1)*7) as date),
cast(dateadd(wk,datediff(wk,0,'01/04/'+iso.yr),0)+((iso.wk)*7)-1 as date))) v(start_dt, end_dt);
yr mo wk start_dt end_dt
2020 12 50 2020-12-07 2020-12-13
2020 12 51 2020-12-14 2020-12-20
2020 12 52 2020-12-21 2020-12-27
2020 12 53 2020-12-28 2021-01-03
2021 01 01 2021-01-04 2021-01-10
2021 01 02 2021-01-11 2021-01-17
2021 01 03 2021-01-18 2021-01-24
To expand the week ranges into days and then count by calendar year and calendar month you could try something like this.
[Edit] It's my understanding the date hierarchy you're looking for is 1) calendar year, 2) calendar month, 3) iso week. The output seems to match the example now. However, there's not a way to ORDER BY to display like the example.
with
iso_dts_cte(yr, mo, wk) as (
select * from (values ('2020', '12', '50'),
('2020', '12', '51'),
('2020', '12', '52'),
('2020', '12', '53'),
('2021', '01', '01'),
('2021', '01', '02'),
('2021', '01', '03')) v(yr, mo, wk)),
days_cte(n) as (
select * from (values (1),(2),(3),(4),(5),(6),(7)) v(n))
select year(dt.calc_dt) cal_yr, month(dt.calc_dt) cal_mo, iso.wk, count(*) day_count
from iso_dts_cte iso
cross apply (values (cast(dateadd(wk,datediff(wk,0,'01/04/'+iso.yr),0)+((iso.wk-1)*7) as date),
cast(dateadd(wk,datediff(wk,0,'01/04/'+iso.yr),0)+((iso.wk)*7)-1 as date))) v(start_dt, end_dt)
cross join days_cte d
cross apply (values (dateadd(day, d.n-1, v.start_dt))) dt(calc_dt)
group by year(dt.calc_dt), month(dt.calc_dt), iso.wk;
cal_yr cal_mo wk day_count
2020 12 50 7
2020 12 51 7
2020 12 52 7
2020 12 53 4
2021 1 01 7
2021 1 02 7
2021 1 03 7
2021 1 53 3
I would assume you could use:
Select DatePart(weekday, '2021/01/01')
and loop for each months, the value here is 5 as the day of the week is Friday, thus there is 3 days for that first week and 4 days for the previous month last week since there is 7 days per week.
If you have the week number 1 to 53 and the year, if it's the first or last week of the month, you need to calculate using the weekday logic written above. Otherwise it's 7.
You could create a scalar function with a similar logic and create your table using the function in it. I believe you are expecting something simpler which nothing come to mind right now.
You can also use "SET DATEFIRST 1;" to change what is considered the first day of the week (Monday / Sunday).
Ended up creating a proof of concept:
CREATE FUNCTION calculateWeekDays(#y int,#m int,#w int)
RETURNS INT
AS BEGIN
DECLARE #numberOfDays INT = 7;
DECLARE #firstDayOfMonth datetime = DATEFROMPARTS (#y, #m, 1);
DECLARE #nextMonth datetime = (SELECT DATEADD(MONTH, 1, #firstDayOfMonth));
DECLARE #lastDayOfMonth datetime = (SELECT DATEADD(DAY, -1, #nextMonth));
DECLARE #weekOfYearStartOfMonth INT = (select DatePart(week, #firstDayOfMonth));
DECLARE #weekOfYearEndOfMonth INT = (select DatePart(week, #lastDayOfMonth));
DECLARE #firstWeekOfMonth INT = (select datediff(week, dateadd(week, datediff(day,0,dateadd(month, datediff(month,0,#firstDayOfMonth),0))/7, 0),#firstDayOfMonth-1) + 1);
DECLARE #lastWeekOfMonth INT = (select datediff(week, dateadd(week, datediff(day,0,dateadd(month, datediff(month,0,#lastDayOfMonth),0))/7, 0),#lastDayOfMonth-1) + 1);
IF #w - #weekOfYearStartOfMonth % 52 = 0
SET #numberOfDays = (select 8-DatePart(weekday, #firstDayOfMonth));
ELSE IF #w = #weekOfYearEndOfMonth
SET #numberOfDays = (select DatePart(weekday, #nextMonth)-1);
RETURN #numberOfDays;
END
GO
from that scalar function you can get the result using:
select yr, mo, wk, dbo.calculateWeekDays(yr,mo,wk) as wd from (
values (2020, 12, 50),
(2020, 12, 51),
(2020, 12, 52),
(2020, 12, 53),
(2021, 01, 01),
(2021, 01, 02),
(2021, 01, 03),
(2021, 03, 13),
(2021, 03, 14),
(2021, 04, 14),
(2021, 04, 15)) v(yr, mo, wk)
it give the following result:
year
mo
wk
wd
2020
12
50
7
2020
12
51
7
2020
12
52
7
2020
12
53
4
2021
1
1
3
2021
1
2
7
2021
1
3
7
----
--
--
--
2021
3
13
7
2021
3
14
3
2021
4
14
4
2021
4
15
7
I have a data where i need to give the counts of the rows for which the confidence code changed.
Input data:
ID Date ReMatchConfidence OrgMatch
1 2017 101 45
2 2017 101 88
3 2017 103 35
4 2016 104 66
5 2016 104 66
6 2017 104 66
7 2016 88 14
8 2017 88 25
Output:
Data 2017 2016
Change from 45 to 101 1 0
Change from 88 to 101 1 0
Change from 35 to 103 1 0
Change from 66 to 104 1 2
Change from 14 to 88 0 1
Change from 25 to 88 1 0
Try this:
SELECT CONCAT('Change from ', OrgMatch, ' to ', ReMatchConfidence) AS Data,
Count(IF(Date = '2017', 1, NULL)) as '2017',
Count(IF(Date = '2016', 1, NULL)) as '2016'
FROM tables GROUP BY OrgMatch, ReMatchConfidence;
A little modification to Daria's query. Works in SQL Server
SELECT CONCAT('Change from ', OrgMatch, ' to ', [RefMatch Confidence]) AS Data,
Count(CASE WHEN [Date] = '2017' THEN 1 ELSE NULL END) as '2017',
Count(CASE WHEN [Date] = '2016' THEN 1 ELSE NULL END ) as '2016'
FROM t1 GROUP BY OrgMatch, [RefMatch Confidence];
I have two tables: The 1st table contains a unique identifier (UI). Each unique identifier has a column containing a start date (yyyy-mm-dd), and a column containing an end date (yyyy-mm-dd). The 2nd table contains the temperature for each day, with separate columns for the month, day, year and temperature. I would like to join those tables and get the compiled temperature for each unique identifier; however I would the compiled temperature to only include the days from the second table that fall between start and end dates from the 1st table.
For example, if one record has a start_date of 12/10/15 and an end date of 12/31/15, I would like to have a column containing compiled temperatures for the 10th-31s. If the next record has a start date 12/3/15-12/17/15, I'd like the column next to it to show the compiled temperature for the 3rd-17th. I'll include the query I have so far, but it is not too helpful because I have not really gotten very far:
; with Temps as (
select MONTH, DAY, YEAR, Temp
from Temperatures
where MONTH = 12
and YEAR = 2016
)
Select UI, start_date, end_date, location, SUM(temp)
from Table1 t1
Inner join Temps
on temps.month = month(t1.start_date)
I appreciate any help you might be able to give. Let me know I need to elaborate on anything.
Table 1
UI Start_Date End_Date
2080 12/5/2015 12/31/2015
1266 12/1/2015 12/31/2015
1787 12/17/2015 12/28/2015
1621 12/3/2015 12/20/2015
1974 12/10/2015 12/12/2015
1731 12/25/2015 12/31/2015
Table 2
Month Day Year Temp
12 1 2016 34
12 2 2016 32
12 3 2016 35
12 4 2016 37
12 5 2016 32
12 6 2016 30
12 7 2016 31
12 8 2016 36
12 9 2016 48
12 10 2016 42
12 11 2016 33
12 12 2016 41
12 13 2016 31
12 14 2016 29
12 15 2016 46
12 16 2016 48
12 17 2016 38
12 18 2016 29
12 19 2016 45
12 20 2016 37
12 21 2016 48
12 22 2016 46
12 23 2016 44
12 24 2016 45
12 25 2016 35
12 26 2016 44
12 27 2016 29
12 28 2016 38
12 29 2016 29
12 30 2016 35
12 31 2016 40
Table 3 (Expected Result)
UI Start_Date End_Date Compiled Temp
2080 12/5/2015 12/31/2015 1101
1266 12/1/2015 12/31/2015 1167
1787 12/17/2015 12/28/2015 478
1621 12/3/2015 12/20/2015 668
1974 12/10/2015 12/12/2015 126
1731 12/25/2015 12/31/2015 250
You could do something like this:
; WITH temps AS (
SELECT CONVERT(DATE, CONVERT(CHAR(4), [YEAR]) + '-' + CONVERT(CHAR(2), [MONTH]) + '-' + CONVERT(VARCHAR(2), [DAY])) [TDate], [Temp]
FROM Temperatures
WHERE [MONTH] = 12
AND [YEAR] = 2015
)
SELECT [UI], [start_date], [end_date]
, (SELECT SUM([temp])
FROM temps
WHERE [TDate] BETWEEN T1.[start_date] AND T1.[end_date]) [Compiled Temp]
FROM Table1 T1
No need for a join.
You can do a simple join of the two tables as well. You don't need to use a CTE.
--TEST DATA
if object_id('Table1','U') is not null
drop table Table1
create table Table1 (UI int, Start_Date date, End_Date date)
insert Table1
values
(2080,'12/05/2015','12/31/2015'),
(1266,'12/01/2015','12/31/2015'),
(1787,'12/17/2015','12/28/2015'),
(1621,'12/03/2015','12/20/2015'),
(1974,'12/10/2015','12/12/2015'),
(1731,'12/25/2015','12/31/2015')
if object_id('Table2','U') is not null
drop table Table2
create table Table2 (Month int, Day int, Year int, Temp int)
insert Table2
values
(12,1, 2015,34),
(12,2, 2015,32),
(12,3, 2015,35),
(12,4, 2015,37),
(12,5, 2015,32),
(12,6, 2015,30),
(12,7, 2015,31),
(12,8, 2015,36),
(12,9, 2015,48),
(12,10,2015,42),
(12,11,2015,33),
(12,12,2015,41),
(12,13,2015,31),
(12,14,2015,29),
(12,15,2015,46),
(12,16,2015,48),
(12,17,2015,38),
(12,18,2015,29),
(12,19,2015,45),
(12,20,2015,37),
(12,21,2015,48),
(12,22,2015,46),
(12,23,2015,44),
(12,24,2015,45),
(12,25,2015,35),
(12,26,2015,44)
--AGGREGATE TEMPS
select t1.Start_Date, t1.End_Date, avg(t2.temp) AvgTemp, sum(t2.temp) CompiledTemps
from table1 t1
join table2 t2 ON t2.Year between datepart(year, t1.Start_Date) and datepart(year, t1.End_Date)
and t2.Month between datepart(month,t1.Start_Date) and datepart(month,t1.End_Date)
and t2.Day between datepart(day, t1.Start_Date) and datepart(day, t1.End_Date)
group by t1.Start_Date, t1.End_Date
How can I make a static column/row in crosstab? See example below; can I have a fixed jan, feb, march, ... columns instead of it generating dynamically?
location jan feb mar apr may jun jul aug sep oct nov dec
london 500 62 200 50 0 60 100 46 89 200 150 210
paris 50 26 20 500 50 70 40 200 0 40 250 50
I want the column (jan, feb, mar, apr, ...) to always show up regardless of their measures zero or have values. Like they are fixed.
Here is the query I'm using:
select sum("AMOUNT"), "REQUESTDATE","description"
from(
SELECT SUM(e.AMOUNT)"AMOUNT",TO_CHAR(REQUESTDATE,'MM')"REQUESTDATE", CA.DESCR "description"
FROM PC_PAYMENTTRXNLOG PC,GLB_TYPE ca, PC_ESERVICEINQUIRY e
where PC.ESERVICE_ID = E.ID
AND trunc(REQUESTDATE) between trunc(to_date('2012-01-01','yyyy-mm-dd')) and trunc(to_date('2012-06-30','yyyy-mm-dd'))
GROUP BY TO_CHAR(REQUESTDATE,'MM'),CA.DESCR
)
group by "REQUESTDATE","description"
and the output
SUM("amount") Requestdate Description
2550405 04 A
2550405 04 B
23893281 05 C
614977 06 A
614977 06 E
2550405 04 C
now after updated the query to be
select sum("AMOUNT"), month,"description"
from(
SELECT SUM(e.AMOUNT)"AMOUNT",TO_CHAR(REQUESTDATE,'MM')"REQUESTDATE", CA.DESCR "description"
FROM PC_PAYMENTTRXNLOG PC,GLB_TYPE ca, PC_ESERVICEINQUIRY e
where PC.ESERVICE_ID = E.ID
AND trunc(REQUESTDATE) between trunc(to_date('2012-01-01','yyyy-mm-dd')) and trunc(to_date('2012-06-30','yyyy-mm-dd'))
GROUP BY TO_CHAR(REQUESTDATE,'MM'),CA.DESCR
)
full outer join (select to_char(date '1970-01-01'
+ numtoyminterval(level - 1, 'month'), 'mm') as month
from dual
connect by level <= 12) on month="REQUESTDATE"
group by month,"description"
when run the query run it displaying all the months regardless of their measures zero or have values.
BUT now the output is like that
location jan feb mar apr may jun jul aug sep oct nov dec
london 500 62 200 50 0 60 100 46 89 200 150 210
paris 50 26 20 500 50 70 40 200 0 40 250 50
null 0 0 0 0 0 0 0 0 0 0 0 0
how i can restrict/hide the last null row?
have not tested it.. but try something like this
select sum("AMOUNT"), month,"description"
from(SELECT SUM(e.AMOUNT)"AMOUNT",TO_CHAR(REQUESTDATE,'MM')"REQUESTDATE", CA.DESCR "description"
FROM PC_PAYMENTTRXNLOG PC,GLB_TYPE ca, PC_ESERVICEINQUIRY e
where PC.ESERVICE_ID = E.ID
AND trunc(REQUESTDATE) between trunc(to_date('2012-01-01','yyyy-mm-dd')) and trunc(to_date('2012-06-30','yyyy-mm-dd'))
GROUP BY TO_CHAR(REQUESTDATE,'MM'),CA.DESCR
)
full outer join (select to_char(date '1970-01-01'
+ numtoyminterval(level - 1, 'month'), 'mm') as month
from dual
connect by level <= 12) on month="REQUESTDATE"
group by month,"description"
click here for SQL Fiddle demo to generate 1 to 12 in Oracle
Once you have generated this.. full outer join Your main query with this series query and take month from series query as I did in main query.
Using this query you will get all the data for all months with null values in measure.
For Description column - iReport set property's isRemoveLineWhenBlank and isBlankWhenNull to True, This will remove the null value being printed in iReport
For Measure use Print when expression in such a way, when ever description is null then return false. So this will prevent the value 0 being printed in iReport.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions must demonstrate a minimal understanding of the problem being solved. Tell us what you've tried to do, why it didn't work, and how it should work. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have a table with data in my database as follows:
year month sales mom qoq yoy
----------------
2010 1 80 - - -
2010 2 61 -23.75% - -
2010 3 81 32.79% - -
2010 4 94 16.05% -
2010 5 77 -18.09% -
2010 6 75 -2.60% -
2010 7 58 -22.67% -
2010 8 74 27.59% -
2010 9 98 32.43% -
2010 10 97 -1.02% -
2010 11 94 -3.09% -
2010 12 63 -32.98% -
2011 1 61 -3.17% -23.75%
2011 2 79 29.51% 29.51%
2011 3 84 6.33% 3.70%
2011 4 100 19.05% 6.38%
2011 5 78 -22.00% 1.30%
2011 6 99 26.92% 32.00%
2011 7 78 -21.21% 34.48%
2011 8 63 -19.23% -14.86%
2011 9 66 4.76% -32.65%
2011 10 77 16.67% -20.62%
2011 11 93 20.78% -1.06%
2011 12 94 1.08% 49.21%
I want to calculate monthly, quarterly and yearly sales percentage change for each month period (each row), as shown in last 3 columns above.
How can i achieve this in one result set using T-SQL? I'm using SQL Server 2008 R2. Thanks.
This query works only in MSSQL2012, but it's plan and exec time are much better
SELECT
year,
month,
sales,
(sales - LAG(sales, 1) over (ORDER BY year, month)) / LAG(sales, 1) over (ORDER BY year, month)*100 AS mom,
(sales - LAG(sales, 4) over (ORDER BY year, month)) / LAG(sales, 4) over (ORDER BY year, month)*100 AS qoq,
(sales - LAG(sales, 12) over (ORDER BY year, month)) / LAG(sales, 12) over (ORDER BY year, month)*100 AS yoy
FROM #tab
It would be nice to use window function with LAG here, but it works only in MSSQL2012. So I use row_number
declare #tab table (year int, month int, sales money)
insert into #tab values
(2010, 1, 80 ),(2010, 2, 61 ),(2010, 3, 81 ),
(2010, 4, 94 ),(2010, 5, 77 ),(2010, 6, 75 ),
(2010, 7, 58 ),(2010, 8, 74 ),(2010, 9, 98 ),
(2010, 10, 97 ),(2010, 11, 94 ),(2010, 12, 63 ),
(2011, 1, 61 ),(2011, 2, 79 ),(2011, 3, 84 ),
(2011, 4, 100),(2011, 5, 78 ),(2011, 6, 99 ),
(2011, 7, 78 ),(2011, 8, 63 ),(2011, 9, 66 ),
(2011, 10, 77 ),(2011, 11, 93 ),(2011, 12, 94 );
with cte as (
select
row_number() over (order by year, month) rn,
year,
month,
sales
from #tab
)
select
year,
month,
sales,
round((sales-(select sales from cte where cte.rn=t1.rn-1))/(select sales from cte where cte.rn=t1.rn-1)*100.0,2) as mom,
round((sales-(select sales from cte where cte.rn=t1.rn-4))/(select sales from cte where cte.rn=t1.rn-4)*100.0,2) as qoq,
round((sales-(select sales from cte where cte.rn=t1.rn-12))/(select sales from cte where cte.rn=t1.rn-12)*100.0,2) as yoy
from cte as t1