I have a table, an example shown below, that lists people and their birth and death years (9999 indicates an alive person).
person | born | died
1 | 2001 | 9999
2 | 2002 | 2003
3 | 2002 | 2008
4 | 2004 | 2009
5 | 2005 | 9999
6 | 2010 | 2012
I want to compute the population over the years. For the example shown above, the output is going to be the following. The list of persons alive at the end of the rows is for demonstration purposes only, I don't need that in my output.
year | population
2001 | 1 (1)
2002 | 3 (1,2,3)
2003 | 2 (1,3)
2004 | 3 (1,3,4)
2005 | 4 (1,3,4,5)
2006 | 4 (1,3,4,5)
2007 | 4 (1,3,4,5)
2008 | 3 (1,4,5)
2009 | 2 (1,5)
2010 | 3 (1,5,6)
2011 | 3 (1,5,6)
2012 | 2 (1,5)
I am able to get this writing a C# program and looping through each of the rows in the original table and incrementing the population count of each of the years that fall between the birth and death years. However, I need to do it entirely in a TSQL script. I am a TSQL newbie. How do I go about it? If a similar problem has already been discussed, please point me to it. Thank you.
DECLARE #t TABLE(person INT, born INT, died INT);
INSERT #t VALUES
(1,2001,9999),
(2,2002,2003),
(3,2002,2008),
(4,2004,2009),
(5,2005,9999),
(6,2010,2012);
DECLARE #start INT, #end INT;
SELECT #start = 2001, #end = 2012;
;WITH n(y) AS
(
SELECT TOP (#end - #start + 1)
#start - 1 + ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
)
SELECT
[year] = n.y,
population = SUM(CASE WHEN n.y >= t.born AND n.y < t.died THEN 1 ELSE 0 END)
FROM n CROSS JOIN #t AS t GROUP BY n.y;
Results:
year population
---- ----
2001 1
2002 3
2003 2
2004 3
2005 4
2006 4
2007 4
2008 3
2009 2
2010 3
2011 3
2012 2
FWIW I just started a blog series on this very thing:
http://www.sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1
Related
I just want to ask you guys, especially those with MsSQL knowledge, regarding my query.
My goal is to get the average delivery time and group my data by delivery date and route id daily/weekly/monthly.
Here's my query:
SELECT
RouteID,
CONVERT(date, [DeliveryDate]) AS delivery_date,
AVG(
DATEDIFF(
day,
CONVERT(date, [UnloadDate]),
CONVERT(date, [DeliveryDate])
)
) as Averate_Delivery_Time
FROM [CARGODB].[dbo].[Cargo_Transactions]
WHERE
[DeliveryDate] IS NOT NULL AND
[UnloadDate] != 0 AND
[StageID] = 'D' AND
( CONVERT(date, [DeliveryDate]) LIKE '%2016%' or
CONVERT(date, [DeliveryDate]) LIKE '%2017%')
GROUP BY CONVERT(date, [DeliveryDate]), [RouteID]
ORDER BY CONVERT(date, [DeliveryDate]) DESC
I am not confident if the average delivery time is correct so if you think it's wrong or there are other things in my query that needs to be corrected, please let me know.
UPDATE:
I was able to get the right query:
SELECT [RouteID],
CAST(DATEPART(YEAR,[DeliveryDate]) as varchar) + ' Week ' +
CAST(DATEPART(WEEK,[DeliveryDate]) AS varchar) AS week_name,
AVG(DATEDIFF(day, CONVERT(date, [UnloadDate]), CONVERT(date,
[DeliveryDate]))) as Average_Delivery_Days
FROM [CARGODB].[dbo].[Cargo_Transactions]
WHERE [DeliveryDate] IS NOT NULL AND [DeliveryDate] != 0
AND CONVERT(date, [DeliveryDate]) BETWEEN '2016-01-01' AND GETDATE()
AND [UnloadDate] IS NOT NULL AND [UnloadDate] != 0 AND [DeliveryDate] >
[UnloadDate]
AND [Deleted] = 0 and [StageID] = 'D'
GROUP BY DATEPART(YEAR,[DeliveryDate]), DATEPART(WEEK,[DeliveryDate]),
[RouteID]
ORDER BY DATEPART(YEAR,[DeliveryDate]), DATEPART(WEEK,[DeliveryDate]),
Average_Delivery_Days desc
But I have a more complicated query to do now. I have this sample data:
RouteID | week_name | yearnum | weeknum | Average_Delivery_Days
=======================================================================
MK | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
TSM | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
E | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
A | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
D | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
MP | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
CTN | 2016 Week 3 | 2016 | 3 | 9
-----------------------------------------------------------------------
BIS | 2016 Week 3 | 2016 | 3 | 8
-----------------------------------------------------------------------
C | 2016 Week 3 | 2016 | 3 | 1
-----------------------------------------------------------------------
PN | 2016 Week 4 | 2016 | 4 |10
-----------------------------------------------------------------------
How can I make the above data be like:
MK and TSM are merged into 1 new routeID like Manila1
E, A, and D are merged into another as Manila2
MP, CTN, AND BIS as Visayas
C and PN as Mindanao
and so on..
And the average delivery days will be changed as well.
Your help is highly appreciated. Thank you!
I'm using SQL Server 2014. I have a Claims table containing totals of claims made per month in my system:
+-----------+-------------+------------+
| Claim_ID | Claim_Date | Nett_Total |
+-----------+-------------+------------+
| 1 | 31 Jan 2012 | 321454.67 |
| 2 | 29 Feb 2012 | 523542.34 |
| 3 | 31 Mar 2012 | 35344.33 |
| 4 | 30 Apr 2012 | 142355.63 |
| etc. | etc. | etc. |
+-----------+-------------+------------+
For a report I am writing I need to be able to produce a cumulative running total that resets to zero at the start of each fiscal year (in my country this is from March 1 to February 28/29 of the following year).
The report will look similar to the table, with an extra running total column, something like:
+-----------+-------------+------------+---------------+
| Claim_ID | Claim_Date | Nett_Total | Running Total |
+-----------+-------------+------------+---------------+
| 1 | 31 Jan 2012 | 321454.67 | 321454.67 |
| 2 | 29 Feb 2012 | 523542.34 | 844997.01 |
| 3 | 31 Mar 2012 | 35344.33 | 35344.33 | (restart at 0
| 4 | 30 Apr 2012 | 142355.63 | 177699.96 | for new yr)
| etc. | etc. | etc. | |
+-----------+-------------+------------+---------------+
I know windowing functions are very powerful and I've used them in rudimentary ways in the past to get overall sums and averages while avoiding needing to group my resultset rows. I have an intuition that I will need to employ the 'preceding' keyword to get the running total for the current fiscal year each row falls into, but I can't quite grasp how to express the fiscal year as a concept to use in the 'preceding' clause (or if indeed it's possible to use a date range in this way).
Any assistance on the way of "phrasing" the fiscal year for the "preceding" clause will be of enormous help to me, please.
i think you should try this:
/* Create Table*/
CREATE TABLE dbo.Claims (
Claim_ID int
,Claim_Date datetime
,Nett_Total decimal(10,2)
);
/* Insert Testrows*/
INSERT INTO dbo.Claims VALUES
(1, '20120101', 10000)
,(2, '20120202', 10000)
,(3, '20120303', 10000)
,(4, '20120404', 10000)
,(5, '20120505', 10000)
,(6, '20120606', 10000)
,(7, '20120707', 10000)
,(8, '20120808', 10000)
Query the Data:
SELECT Claim_ID, Claim_Date, Nett_Total, SUM(Nett_Total) OVER
(PARTITION BY YEAR(DATEADD(month,-2,Claim_Date)) ORDER BY Claim_ID) AS
[Running Total] FROM dbo.Claims
The Trick: PARTITION BY YEAR(DATEADD(month,-2,Claim_Date))
New Partition by year, but i change the date so it fits your fiscal year.
Output:
Claim_ID |Claim_Date |Nett_Total |Running Total
---------+---------------------------+------------+-------------
1 |2012-01-01 00:00:00.000 |10000.00 |10000.00
2 |2012-02-02 00:00:00.000 |10000.00 |20000.00
3 |2012-03-03 00:00:00.000 |10000.00 |10000.00 <- New partition
4 |2012-04-04 00:00:00.000 |10000.00 |20000.00
5 |2012-05-05 00:00:00.000 |10000.00 |30000.00
6 |2012-06-06 00:00:00.000 |10000.00 |40000.00
7 |2012-07-07 00:00:00.000 |10000.00 |50000.00
8 |2012-08-08 00:00:00.000 |10000.00 |60000.00
I need to create a query which returns years and months from april 2013 to now like following:
2013 | 4
2013 | 5
2013 | 6
2013 | 7
2013 | 8
.... | .
.... | .
.... | .
2015 | 1
2015 | 2
2015 | 3
2015 | 4
It must works with SQLServer 2000, and all solution I found on SE are only SQLServer2005+ compatible.
Is there a way to do it with this (very) old SGBD?
If there is no solution I would consider to create a table of numbers but I don't find it really elegant...
declare #Startdate AS DateTime = Dateadd( year,-2,Getdate()) ,
#EndDate DateTime = Getdate()
Declare #YearTable as Table ( CalYear int , calmonth int)
While #Startdate <= #EndDate
BEGIN
Insert Into #YearTable values(YEAR(#Startdate),MONTH(#Startdate))
set #Startdate = DateADD(Month,1,#Startdate)
END
select * from #YearTable
i need a query to find the date based on year, month, day of the week and weekday number. Say for example, if the question is to find the date of 2nd Sunday of January 2010, the answer should be '2010-01-10'.
Inputs are
Yr | Mon | Dy | Dyno
-----------------------
2010 | Jan | Sun | 2
2005 | Jan | Mon | 3
1995 | Feb | Sun | 1
2000 | Feb | Wed | 4
1982 | Mar | Tue | 2
2010 | Mar | Tue | 8
Dyno states dayno
The easiest answer to many date-related questions in SQL is to create a calendar table. In your case, if you create a table with the columns you've already shown, and an extra one with the DATETIME value that you want (call it BaseDate), you can get the value you need with a simple query:
select BaseDate
from dbo.Calendar
where Yr = 2010 and Mon = 'Jan' and Dy = 'Sunday' and Dyno = 2
Of course, your calendar table can have 10, 20 or more columns, depending on what values you find useful for your queries.
I have table that has a column of type DateTime, I would like to pivot on the datetime column so that the dates are displayed as columns (as days of the week Monday, Tuesday etc).
So for example I have a table like this (can't remember how SQL Server displays full datetimes but you get the idea):
BugNo | DateOpened | TimeSpent
1234 | 16/08/2010 | 1.0
4321 | 16/08/2010 | 3.5
9876 | 17/08/2010 | 1.5
6789 | 18/08/2010 | 7.0
6789 | 19/08/2010 | 6.5
6789 | 20/08/2010 | 2.5
I would like to pivot on the DateOpened column to create a result set like this
|TimeSpentOnBugByDay| Mon | Tue | Wed | Thu | Fri | Sat | Sun
1234 1
4321 3.5
9876 1.5
6789 7.0
6789 6.5
6789 2.5
I should point out that I'll only be retrieving one week at a time.
I'm not sure if this is possible, though I'm pretty certain I have seen something like this before (that I didn't write).
You can do this
SELECT BugNo, [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday], [Sunday]
FROM (
SELECT BugNo, DATENAME(dw, DateOpened) AS DayWeek, TimeSpent
FROM Bugs
) AS src
pivot (
SUM(TimeSpent) FOR DayWeek IN ([Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday], [Sunday])
) AS pvt