I have a single table of payments called PYMT, and am trying to wrap my head around using a PIVOT if possible to get a certain arrangement for an output and befuddled as how to do this. In the table are pymt_amount and pymt_date (other columns too, but not necessary). I wish to see the output to look like so:
PayMonth 2007 2008 2009 2010 2011
1 26044.12 82663.79 83583.17 35963.49 100865.94
2 60145.61 35245.06 19173.08 14417.98 21502.71
3 68138.88 88670.16 85319.66 40850.39 31595.43
4 228835.04 215258.84 157905.56 136551.46 166027.30
5 395877.88 348307.58 348506.09 363460.24 298488.22
6 618013.05 662869.88 522233.48 472174.95 385879.94
7 557751.27 363659.66 305363.68 304606.98 349173.75
8 355639.91 173107.60 266235.54 147731.54 251878.49
9 131440.63 173338.90 133869.36 140035.13 109595.83
10 168148.90 127356.25 114818.69 119082.52 139201.50
11 139543.35 138151.22 128667.58 137351.77 107807.27
12 142286.06 136670.64 116980.04 69609.22 85670.84
To get the first column of payment totals is easy - it's doing it for the other years that I can't figure out - I know how to do a basic PIVOT table.
The query for the first 2 columns is
SELECT DATEPART(MM, pymt_Date) AS PayMonth, SUM(pymt_Amount) AS [2007]
FROM PYMT
GROUP BY DATEPART(MM, pymt_Date) , DATEPART(YY, pymt_Date)
HAVING (DATEPART(YY, pymt_Date) = 2007)
ORDER BY PayMonth
How to add the other years (each payMonth is a sum of all payments for the month), but I don't wish to pivot by month (just the years by the month as I show in the output)
I could run a separate query in a cursor per month
Here's an example, but showing the grand total for the year (but it needs to be separated by month)
SELECT * FROM
(SELECT DATEPART(yy, pymt_Date) AS PayYear, pymt_Amount
FROM PYMT
) tbl1
PIVOT
(SUM(pymt_Amount)
FOR
PayYear in ([2007],[2008],[2009],[2010],[2011])
) tbl2
which yields
2007 2008 2009 2010 2011
2891864.70 2545299.58 2282655.93 1981835.67 2047687.22
As you can see - this isn't broken down by month rows
Any ideas?
You missed this DATEPART(month, pymt_Date) AS PayMonth in the tbl1 query.
The complete query should be
SELECT *
FROM
(
SELECT DATEPART(year, pymt_Date) AS PayYear,
DATEPART(month, pymt_Date) AS PayMonth,
pymt_Amount
FROM PYMT
) tbl1
PIVOT
(
SUM(pymt_Amount)
FOR PayYear in ([2007],[2008],[2009],[2010],[2011])
) tbl2
Related
Question: need help with query to show as a 3-month rolling average
This is what I currently have
SELECT
Date,
month(date) AS[Month],
year(date) AS[Year],
AVG(ALL Amount) OVER (PARTITION BY Date ORDER BY Date ASC) AS Average
FROM
FactFinance
SELECT
YEAR(Date) AS Year,
MONTH(Date) AS Month,
AVG(Amount) AS AvgAmt
FROM
FactFinance
GROUP BY
YEAR(Date),
MONTH(Date)
ORDER BY
Year,
Month;
GO;
SQL Statement 2
SQL Statement 1
2 things I would do to test this 1) pick a year to make the data more digestible 2) amend the second query to include a sum. The first query should be amended to pre-calculate the the monthly figures using a cte or a sub query (I have chosen to use a sub query) and you need to use the preceding.. clause to tell sql server over how many months you want
select month,year,
sum(amt) over (order by month,year ROWS BETWEEN 2 PRECEDING AND current row) sumamount,
avg(amt) over (order by month,year ROWS BETWEEN 2 PRECEDING AND current row) '3monthra'
from
(
SELECT month(date) as [Month],
year(date) as [Year],
sum(Amount) amt,
count(*) as cnt
FROM FactFinance
GROUP BY YEAR([Date]), MONTH([Date])
) s
where year = 2005
order by year, month
SELECT
YEAR(Date) AS Year,
MONTH(Date) AS Month,
sum(amount) as sumamt,
count(*) as cnt,
AVG(Amount) AS AvgAmt
FROM FactFinance
where YEAR(Date) = 2005
GROUP BY YEAR(Date), MONTH(Date)
ORDER BY Year, Month;
GO
month year sumamount 3monthra
----------- ----------- ---------------------- ----------------------
7 2005 11384884.51 11384884.51
8 2005 36016653.13 18008326.565
9 2005 58029544.31 19343181.4366667
10 2005 66734589.35 22244863.1166667
11 2005 79778854.28 26592951.4266667
12 2005 88791927.09 29597309.03
(6 row(s) affected)
Year Month sumamt cnt AvgAmt
----------- ----------- ---------------------- ----------- ----------------------
2005 7 11384884.51 1130 10075.1190353982
2005 8 24631768.62 1122 21953.4479679145
2005 9 22012891.18 1116 19724.8128853047
2005 10 20089929.55 1122 17905.463057041
2005 11 37676033.55 1124 33519.6028024911
2005 12 31025963.99 1126 27554.1420870338
(6 row(s) affected)
Note this is taken from aw2012 , hopefully your version has 2005.
I am editing this to clarify my question.
Let's say I have a table that holds patient information. I need to find new patients for this year, and the date of their prescription first prescription when they were considered new. Anytime there is a six month gap they are considered a new patient.
How do I accomplish this using SQL. I can do this in Java and any other imperative language easily enough, but I am having problems doing this in SQL. I need this script to be run in Crystal by non-SQL users
Table:
Patient ID Prescription Date
-----------------------------------------
1 12/31/16
1 03/13/17
2 10/10/16
2 05/11/17
2 06/11/17
3 01/01/17
3 04/20/17
4 01/31/16
4 01/01/17
4 07/02/17
So Patients 2 and 4 are considered new patients. Patient 4 is considered a new patient twice, so I need dates for each time patient 4 was considered new 1/1/17 and 7/2/17. Patients 1 and 3 are not considered new this year.
So far I have the code below which tells me if they are new this year, but not if they had another six month gap this year.
SELECT DISTINCT
this_year.patient_id
,this_year.date
FROM (SELECT
patient_id
,MIN(prescription_date) as date
FROM table
WHERE prescription_date BETWEEN '2017-01-01 00:00:00.000' AND '2017-
12-31 00:00:00.000'
GROUP BY [patient_id]) AS this_year
LEFT JOIN (SELECT
patient_id
,MAX(prescription_date) as date
FROM table
WHERE prescription_date BETWEEN '2016-01-01 00:00:00.000' AND '2016-
12-31 00:00:00.000'
GROUP BY [patient_id]) AS last_year
WHERE DATEDIFF(month, last_year.date, this_year.date) > 6
OR last_year.date IS NULL
Patient 2 in your example does not meet the criteria you specified ... that being said ...
You can try something like this ... untested but should be similar (assuming you can put this in a stored procedure):
WITH ordered AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY [Prescription Date]) rn
FROM table1
)
SELECT o1.[PatientID], DATEDIFF(s, o1.[Prescription Date], o2.[Prescription Date]) diff
FROM ordered o1 JOIN ordered o2
ON o1.rn + 1 = o2.rn
WHERE DATEDIFF(m, o1.[Prescription Date], o2.[Prescription Date]) > 6
Replace table1 with the name of your table.
I assume that you mean the patient has not been prescribed in the last 6 months.
SELECT DISTINCT user_id
FROM table_name
WHERE prescribed_date >= DATEADD(month, -6, GETDATE())
This gives you the list of users that have been prescribed in the last 6 months. You want the list of users that are not in this list.
SELECT DISTINCT user_id
FROM table_name
WHERE user_id NOT IN (SELECT DISTINCT user_id
FROM table_name
WHERE prescribed_date >= DATEADD(month, -6, GETDATE()))
You'll need to amend the field and table names.
I am running SQL Server 2014 and I have the following T-SQL query:
USE MYDATABASE
SELECT *
FROM RESERVATIONLIST
WHERE [MTH] IN ('JANUARY 2015','FEBRUARY 2015')
RESERVATIONLIST mentioned in the code above is a view. The query gives me the following output (extract):
ID NAME DOA DOD Nights Spent MTH
--------------------------------------------------------------------
251 AH 2015-01-12 2015-01-15 3 JANUARY 2015
258 JV 2015-01-28 2015-02-03 4 JANUARY 2015
258 JV 2015-01-28 2015-02-03 2 FEBRUARY 2015
The above output consist of around 12,000 records.
I need to modify my query so that it eliminates all duplicate ID and give me the following results:
ID NAME DOA DOD Nights Spent MTH
--------------------------------------------------------------------
251 AH 2015-01-12 2015-01-15 3 JANUARY 2015
258 JV 2015-01-28 2015-02-03 4 JANUARY 2015
I tried something like this, but it's not working:
USE MYDATABASE
SELECT *
FROM RESERVATIONLIST
WHERE [MTH] IN ('JANUARY 2015', 'FEBRUARY 2015')
GROUP BY [ID]
HAVING COUNT ([MTH]) > 1
Following query will return one row per ID :
SELECT * FROM
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT NULL)) rn FROM RESERVATIONLIST
WHERE [MTH] IN ('JANUARY 2015','FEBRUARY 2015')
) T
WHERE rn = 1
Note : this will return a random row from multiple rows having same ID. IF you want to select some specific row then you have to define it in order by. For e.g. :
SELECT * FROM
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY DOA DESC) rn FROM RESERVATIONLIST
WHERE [MTH] IN ('JANUARY 2015','FEBRUARY 2015')
) T
WHERE rn = 1
definitely, it will return the row having max(DOA).
You are trying to do a GROUP BY statement which IMHO is the right way to go. You should formulate all columns that are a constant, and roll-up the others. Depending on the value of DOD and DOA I can see two solutions:
SELECT ID,NAME,DOA,DOD,SUM([Nights Spent]) as Nights,
min(MTH) as firstRes, max(MTH) as lastRes
FROM RESERVATIONLIST
GROUP BY ID,NAME,DOA,DOD
OR
SELECT ID,NAME,min(DOA) as firstDOA,max(DOD) as lastDOD,SUM([Nights Spent]) as Nights,
min(MTH) as firstRes, max(MTH) as lastRes
FROM RESERVATIONLIST
GROUP BY ID,NAME
I have a table of instances that have a Start Date and an End Date column. Here is a simple example:
ID StartDate EndDate
1 1/8/2015 1/10/2015
2 1/8/2015 1/15/2015
3 2/6/2015 3/2/2015
4 1/6/2015 2/20/2015
5 3/18/2015 4/2/2015
I'm trying to write a query to find out how many unique days occur for a given month, but some of the instances overlap and span multiple months which is making it difficult. The results I want would look something like this:
Month # of days
January 26 (earliest is ID 4 starting 1/6)
February 28 (entire month because of ID 3 and 4)
March 16 (2 days from ID 3, 14 days from ID 5)
April 2 (first 2 days of the month from ID 5)
May 0
Any help would be greatly appreciated. Thanks!!
J,
Please check my SQL script below.
Before you run the script you will realize that I've used a SQL Dates table actually a SQL function which returns a temporary dates table.
You can find the source codes at given tutorial
I also used multiple CTE queries
;with dates as (
select
cast(date as date) date
from [dbo].[DateTable]('1/1/2015','12/31/2015')
), cte as (
select
distinct date
from instances, dates
where dates.date between instances.startdate and instances.enddate
)
select
year(date) year, month(date) month, count(*) dayscount
from cte
group by year(date), month(date)
By the way the March returns 16 days, 2 from one and 14 from other.
I hope the Select statement is useful,
The problem is too complicated;
In my opinion, you need to write a function that counts the number of "unique" days of ranges mentioned in the records.
I didn't write the function, but the design of this new function, "num", is like this:
1- It should get the month and year (named aMonth and aYear).
2- It Finds all records that have at least a day in aYear/aMonth:
(month(startDate)=aMonth and year(startDate)=aYear)
or
(month(endDate)=aMonth and year(endDate)=aYear)
or
(
((month(startDate)<aMonth and year(startDate)=aYear) or (year(startDate)<aYear))
and
((month(endDate)>aMonth and year(endDate)=aYear) or (year(endDate)>aYear))
)
3- Over these records, it should open a cursor, and process the records one by one.
4- While processing each records of the cursor, you can count the days of the month and store them in an array (or 28-31 character string of 0/1, for example).
5- count the number of 1's of this array (or string) and return it.
Having written this function ("num"), The high level of the answer will be like this:
Select 'January', dbo.num(1, 2015) as days
union all
Select 'February', dbo.num(2, 2015) as days
union all
Select 'March', dbo.num(3, 2015) as days
union all
Select 'April', dbo.num(4, 2015) as days
union all
Select 'May', dbo.num(5, 2015) as days
union all
Select 'June', dbo.num(6, 2015) as days
union all
Select 'July', dbo.num(7, 2015) as days
union all
Select 'August', dbo.num(8, 2015) as days
union all
Select 'September', dbo.num(9, 2015) as days
union all
Select 'October', dbo.num(10, 2015) as days
union all
Select 'November', dbo.num(11, 2015) as days
union all
Select 'December', dbo.num(12, 2015) as days
If you count the days for the same year only, you can try this. I only build the code for two months but it's easy to extend it.
SELECT
(SELECT SUM(CASE WHEN sdate>='2015-2-1' OR edate<'2015-1-1' THEN 0
WHEN edate>='2015-2-1' THEN datediff(day, sdate, '2015-2-1')
ELSE datediff(day,sdate,edate) END)
FROM a1)
AS Jan_Days,
(SELECT SUM(CASE WHEN sdate>='2015-3-1' OR edate<'2015-2-1' THEN 0
WHEN edate>='2015-3-1' THEN datediff(day, sdate, '2015-3-1')
ELSE datediff(day,sdate,edate) END)
from a1 )
AS Feb_Days,
...
It's far from efficient. It will be more efficient to use a script or stored procedure running through your records and calculate the results.
I have been trying to find out a solution for this case.
Now I am currently trying to create and retrieve duplicate values in each row in a single table
date date_year, date_month, date_day, weeday_nm, start_time, end_time
2014-10-6 2014 10 6 Monday 2014-10-06 00:00:00.000,2014-10-06 23:59:59.000
2014-10-7 2014 10 7 Tuesday 2014-10-07 00:00:00.000,2014-10-07 23:59:59.000
In output, I would like to see a record
date date_year, date_month, date_day, weeday_nm, start_time, end_time
2014-10-6 2014 10 6 Monday 2014-10-06 00:00:00.000,2014-10-06 23:59:59.000
2014-10-6 2014 10 6 Monday 2014-10-06 00:00:00.000,2014-10-06 23:59:59.000
2014-10-7 2014 10 7 Tuesday 2014-10-07 00:00:00.000,2014-10-07 23:59:59.000
2014-10-7 2014 10 7 Tuesday 2014-10-07 00:00:00.000, 2014-10-07 23:59:59.000'
'
'
'
'
'
there are so many rows is there any way that I can complete this case?
Add a CROSS JOIN to a table with two rows in - easiest way if you are using SQL Server 2008 or later is to use a table valued constructor e.g.
SELECT t.*
FROM YourTable AS t
CROSS JOIN (VALUES (1), (2)) AS cj (Number)
If you are using an earlier version use:
SELECT t.*
FROM YourTable AS t
CROSS JOIN (SELECT Number = 1 UNION ALL SELECT 2) AS cj;
This way you can duplicate the records without having to read the table twice.