sql crosstab for graphing data output - sql-server

I am trying to get a crosstab of data so that the columns are months of the year and the rows are the years themselves based on value sold in that month.
Therefore:
year jan feb mar apr etc
2014 0 1 5 9
2015 11 12 0 14
using this SQL - (excuse the crudeness)
SELECT distinct
case
when year(fsivho.InvoiceDate) = 2014 then 2014
when year(fsivho.InvoiceDate) = 2015 then 2015
when year(fsivho.InvoiceDate) = 2016 then 2016
when year(fsivho.InvoiceDate) = 2017 then 2017
when year(fsivho.InvoiceDate) = 2018 then 2018
when year(fsivho.InvoiceDate) = 2019 then 2019
when year(fsivho.InvoiceDate) = 2020 then 2020
when year(fsivho.InvoiceDate) = 2021 then 2021
when year(fsivho.InvoiceDate) = 2022 then 2022
when year(fsivho.InvoiceDate) = 2023 then 2023
when year(fsivho.InvoiceDate) = 2024 then 2024
when year(fsivho.InvoiceDate) = 2025 then 2025
end as Month1,
(select sum(cast(fsivl.ShipQuantity * fsivl.InvoiceLocalUnitPrice as decimal(8,2))) as Value
from sqlinvline fsivl , sqlinvheaher fsivh
where fsivl.HeaderKey = fsivh.HeaderKey
and fsivh.InvoiceType = 'I'
and year(fsivh.InvoiceDate)* 100 + month(fsivh.InvoiceDate)
= year(fsivho.InvoiceDate)* 100 + month(fsivho.InvoiceDate)
and MONTH(fsivh.InvoiceDate) = 1
) as 'Jan',
(select sum(cast(fsivl.ShipQuantity * fsivl.InvoiceLocalUnitPrice as decimal(8,2))) as Value
from sqlinvline fsivl , sqlinvheaher fsivh
where fsivl.HeaderKey = fsivh.HeaderKey
and fsivh.InvoiceType = 'I'
and year(fsivh.InvoiceDate)* 100 + month(fsivh.InvoiceDate)
= year(fsivho.InvoiceDate)* 100 + month(fsivho.InvoiceDate)
and MONTH(fsivh.InvoiceDate) = 2
) as 'Feb',
from sqlinvline fsivlo , sqlinvheader fsivho
where fsivlo.HeaderKey = fsivho.InvoiceHeaderKey
and fsivho.InvoiceType = 'I'
and year(fsivho.InvoiceDate) >= year(DATEadd(yyyy, -2, GETDATE()))
group by YEAR(fsivho.InvoiceDate)
order by Month1
(I've shortened the code for brevity but the other months are formatted the same way)
When I run it, I get multiple lines for the years with an entry for a value against each month and the rest as nulls...e.g.
year jan feb mar apr etc
2014 nul 1 nul nul
2014 6 nul nul nul
2014 nul nul 7 nul
etc
What am I doing wrong?

all sorted by rewriting the query after many hours looking at pivot examples.
SELECT * from
(select year(fsivho.InvoiceDate) as Yr, cast(datename(MONTH ,fsivho.InvoiceDate) as CHAR(3)) as Mth,
cast(fsivlo.ShipQuantity * fsivlo.InvoiceLocalUnitPrice as decimal(8,2)) as Value
from ARInvoiceLine fsivlo , ARInvoiceHeader fsivho
where fsivlo.HeaderKey = fsivho.HeaderKey
and fsivho.InvoiceType = 'I'
and year(fsivho.InvoiceDate) >= year(DATEadd(yyyy, -4, GETDATE()))) src
PIVOT
(sum(Value)
for Mth in ([Jan],[Feb],[Mar],[Apr],[May],[Jun],[Jul],[Aug],[Sep],[Oct],[Nov],[Dec])) as pvt
order by Yr;
so now have rows of years with months across the top and values in the correct place!

Related

Split string in columns by delimiter using SQL Server

I need to split my one column value in column using delimiter, below is the table structure.
create table #a
(
id int,
timeline varchar(100)
)
insert into #a
values (1, 'Semi Annual Q2 (May/June/July) & Q4 (Nov/Dec/Jan)'),
(2, 'Semi Annual Q1 (Feb/Mar/Apr) & Q3 (Aug/Sep/Oct)'),
(3, 'Annual Q3 (Aug/Sep/Oct)'),
(4, 'Annual Q2 (May/June/July)'),
(5, 'Annual Q4 (Nov/Dec/Jan)'),
(6, 'Semi Annual Q1 (Jan/Feb/Mar) & Q3 (July/Aug/Sep)')
select * from #a
Output I want to split timeline values by ' / ' delimiter and make separate column for separate month and all month should be in sequence, which look like a below sample.
ID M1 M2 M3 M4 M5 M6
---------------------------------------
1 May June July Nov Dec Jan
2 Feb Mar Apr Aug Sep Oct
3 Aug Sep Oct NULL NULL NULL
4 May June July NULL NULL NULL
5 Nov Dec Jan NULL NULL NULL
6 Jan Feb Mar July Aug Sep
So far, I have tried this:
select
timeline,
substring((substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline) - 1)), 1, charindex('/', substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline) - 1)) - 1) as M1,
replace(replace(right(substring(substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline)), 1, charindex(')', substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline)))), charindex('/', reverse(substring(substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline)), 1, charindex(')', substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline))))), 4)), '/', ''), ')', '') as M3
from
#a;
which is not a code and too tedious also. please help if you have efficient way to do this.
The SplitCSVToTable8K function being used in the following solution, is the same DelimitedSplit8K function mentioned above...
Here's how to use it in the solution:
WITH
cte_ParseTimeline AS (
SELECT
a.id,
rn = ROW_NUMBER() OVER (PARTITION BY a.id ORDER BY sc.ItemNumber),
sc.Item
FROM
#a a
CROSS APPLY dbo.SplitCSVToTable8K(REPLACE(REPLACE(a.timeline, '(', '/'), ')', '/'), '/') sc
WHERE
sc.Item LIKE ('[A-Z][a-z][a-z]')
OR
sc.Item LIKE ('[A-Z][a-z][a-z][a-z]')
)
SELECT
pt.id,
M1 = MAX(CASE WHEN pt.rn = 1 THEN pt.Item END),
M2 = MAX(CASE WHEN pt.rn = 2 THEN pt.Item END),
M3 = MAX(CASE WHEN pt.rn = 3 THEN pt.Item END),
M4 = MAX(CASE WHEN pt.rn = 4 THEN pt.Item END),
M5 = MAX(CASE WHEN pt.rn = 5 THEN pt.Item END),
M6 = MAX(CASE WHEN pt.rn = 6 THEN pt.Item END)
FROM
cte_ParseTimeline pt
GROUP BY
pt.id;
Results...
id M1 M2 M3 M4 M5 M6
----------- ----- ----- ----- ----- ----- -----
1 May June July Nov Dec Jan
2 Feb Mar Apr Aug Sep Oct
3 Aug Sep Oct NULL NULL NULL
4 May June July NULL NULL NULL
5 Nov Dec Jan NULL NULL NULL
6 Jan Feb Mar July Aug Sep

Query last 12 months of data where year and month are separate columns

I'm trying to figure out a way to query the last 12 completed months of data, so it would go up to last month and ignore this month.
I've done this before when the table had a date column, but this new table I'm working with separates the date into two month and year columns.
How would I go about this in SQL Server 2012?
Thanks in advance.
SAMPLE DATA:
CalendarYear CalendarMonth TotalSales
2014 3 35.00
2014 4 220.00
2015 2 243.00
2015 5 17.93
2015 6 216.36
2015 10 370.93
2015 12 350.00
2016 1 116.75
2016 2 13.78
DESIRED OUTPUT (assuming current time is in February 2016):
CalendarYear CalendarMonth TotalSales
2015 2 243.00
2015 5 17.93
2015 6 216.36
2015 10 370.93
2015 12 350.00
2016 1 116.75
I've assumed the Day part is always the 1st of the month.
WHERE
CONVERT(DATETIME, CalendarYear + '-' + CalendarMonth + '- 01') >= DATEADD(mm, -12, GETDATE())
The above assumes the columns are strings. Below I've done a version where the columns are numbers.
WHERE
CONVERT(DATETIME, CAST(CalendarYear AS NVARCHAR(4)) + '-' + CAST(CalendarMonth AS NVARCHAR(2)) + '-01') >= DATEADD(mm, -12, GETDATE())
You can just apply same code/logic to year and month you want data.
where to_date(year || month, 'YYYYMM')
between add_months(trunc(sysdate, 'MM'), -12) and trunc(sysdate)

Get last 6 months monthname.month number and Years in simple select statement

How to get last 6 months month name, month number and Years in simple select statement in sqlserver .
The no of months is 6, and is fixed
12 Dec 2015
11 Nov 2015
10 Oct 2015
9 Sep 2015
8 Aug 2015
7 Jul 2015
6 Jun 2015
This should handle year end boundaries
say, if the current month is Feb 2016, the result should give 2015 months.
2 Feb 2016
1 Jan 2016
12 Dec 2015
11 Nov 2015
10 Oct 2015
9 Sep 2015
8 Aug 2015
You can do it with the following:
SELECT MONTH(DATEADD(mm, -m, GETDATE())) AS m,
LEFT(DATENAME(mm, DATEADD(mm, -m, GETDATE())), 3) AS n,
YEAR(DATEADD(mm, -m, GETDATE())) AS y
FROM (VALUES (0),(1),(2),(3),(4),(5),(6)) t(m)
Output:
m n y
12 Dec 2015
11 Nov 2015
10 Oct 2015
9 Sep 2015
8 Aug 2015
7 Jul 2015
6 Jun 2015
Try this
;with cte as
(
select 0 as num
union all
select num+1 from cte where num<6
)
select month(dates),datename(month,dates),year(dates)
from
(
select dateadd(mm,-num,datadd(dd,1,eomonth(getdate(),-1))) as dates
from cte
) A
SQL FIDDLE DEMO
select datepart(m,GETDATE()) MonthNumber,left(datename(month,GETDATE()),3) as Month,year(GETDATE()) as Year union all
select datepart(m,DATEADD(month,-1,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-1,GETDATE())),3) as Month,year(DATEADD(month,-1,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-2,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-2,GETDATE())),3) as Month,year(DATEADD(month,-2,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-3,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-3,GETDATE())),3) as Month,year(DATEADD(month,-3,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-4,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-4,GETDATE())),3) as Month,year(DATEADD(month,-4,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-5,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-5,GETDATE())),3) as Month,year(DATEADD(month,-5,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-6,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-6,GETDATE())),3) as Month,year(DATEADD(month,-6,GETDATE())) as Year
The above query will work for most of the RDBMS.
For SQL Server specific use the below query.
SELECT MONTH(DATEADD(month, -month, GETDATE())) AS MonthNumber ,
LEFT(DATENAME(MONTH, DATEADD(month, -month, GETDATE())), 3) AS MonthName,
YEAR(DATEADD(month, -month, GETDATE())) AS Year
FROM ( VALUES (0), (1), (2), (3), (4), (5),(6) ) t ( month )
Try DATEADD:
Select * FROM Table where YourDate>=DATEADD(m, -6, GETDATE())

SQL Server : select latest time

I have a query that works well but has one problem, it displays other data I don't want.
The data is the latest of each username.
Here is the query:
SELECT
l.USER_KEY AS id,
l.USER_ID AS username,
gl1.CHAR_KEY AS char_id,
gl1.NAME AS charname,
gl1.GATENUM AS server,
CONVERT(VARCHAR(20), l.LOGINTIME, 100) AS user_time,
CONVERT(VARCHAR(20), gl1.OCCUR_TIME, 100) AS char_time
FROM LOG_CONNECT201211 AS gl1
JOIN game.dbo.CHAR_INFOR AS g --character data
ON gl1.CHAR_KEY = g.CHAR0_KEY OR gl1.CHAR_KEY = g.CHAR1_KEY OR gl1.CHAR_KEY = g.CHAR2_KEY
JOIN login.dbo.USER_CHECK_LOGIN AS l --login data
ON g.USER_KEY = l.USER_KEY
JOIN (SELECT char_key, max(OCCUR_TIME) as mostrecent --game logs
FROM LOG_CONNECT201211
WHERE KIND=20 OR KIND=21
GROUP BY char_key) AS gl2
ON gl2.char_key = gl1.char_key and gl2.mostrecent = gl1.OCCUR_TIME
WHERE l.CHECKLOGIN = 1
ORDER BY username DESC
That returns:
id username char_id name map user_time char_time
------------------------------------------------------------------------------------
3667 zr5970 11002 warpath 4 Nov 15 2012 8:54AM Nov 7 2012 6:31AM
3667 zr5970 11004 bloodfines 4 Nov 15 2012 8:54AM Nov 7 2012 6:33AM
3667 zr5970 11003 hanzhou 1 Nov 15 2012 8:54AM Nov 15 2012 8:54AM
14999 yvacosta 52086 Creams 1 Nov 15 2012 8:17AM Nov 15 2012 8:17AM
23433 yurich 1911481 abal 5 Nov 15 2012 8:34AM Nov 9 2012 4:05PM
23433 yurich 1911482 yurich 5 Nov 15 2012 8:34AM Nov 15 2012 8:30AM
23433 yurich 1911483 sharmaine 5 Nov 15 2012 8:34AM Nov 15 2012 8:35AM
10967 yubiwamoi 33376 Dwina 1 Nov 15 2012 4:33AM Nov 15 2012 4:33AM
So the data is correct, but I only want to return one row per username.
On this data the username returns 3, with 3 names, but the only name I want is that one with the latest char_time.
Example correct data:
id username char_id name map user_time char_time
-----------------------------------------------------------------------------------
3667 zr5970 11003 hanzhou 1 Nov 15 2012 8:54AM Nov 15 2012 8:54AM
14999 yvacosta 52086 Creams 1 Nov 15 2012 8:17AM Nov 15 2012 8:17AM
23433 yurich 1911483 sharmaine 5 Nov 15 2012 8:34AM Nov 15 2012 8:35AM
10967 yubiwamoi 33376 Dwina 1 Nov 15 2012 4:33AM Nov 15 2012 4:33AM
Notice that I only displayed the data for zr5970 with the latest char_time
Please advice, Thank you.
Use ROW_NUMBER()
SELECT *
FROM
(
SELECT
l.USER_KEY AS id,
l.USER_ID AS username,
gl1.CHAR_KEY AS char_id,
gl1.NAME AS charname,
gl1.GATENUM AS server,
CONVERT(VARCHAR(20), l.LOGINTIME, 100) AS user_time,
CONVERT(VARCHAR(20), gl1.OCCUR_TIME, 100) AS char_time,
rn = row_number() over (partition by l.USER_KEY order by gl1.OCCUR_TIME DESC)
FROM LOG_CONNECT201211 AS gl1
JOIN game.dbo.CHAR_INFOR AS g --character data
ON gl1.CHAR_KEY = g.CHAR0_KEY OR gl1.CHAR_KEY = g.CHAR1_KEY OR gl1.CHAR_KEY = g.CHAR2_KEY
JOIN login.dbo.USER_CHECK_LOGIN AS l --login data
ON g.USER_KEY = l.USER_KEY
WHERE l.CHECKLOGIN = 1
) X
WHERE rn=1
ORDER BY username DESC
First select just the user name and the max time. This way you will have the correct number of rows. When you have that you can easily the other columns to your query...
select a.USER_KEY AS id,
l.USER_ID AS username,
gl1.CHAR_KEY AS char_id,
gl1.NAME AS charname,
gl1.GATENUM AS server,
CONVERT(VARCHAR(20), l.LOGINTIME, 100) AS user_time,
CONVERT(VARCHAR(20), gl1.OCCUR_TIME, 100) AS char_time
from
(select user_key, max(occur_time) as mostrecent
from log_connect2011
WHERE KIND=20 OR KIND=21
group by user_key) a
join LOG_CONNECT201211 AS gl1 on a.user_key = gl1.user_key and a.mostrecent = gl1.occur_time
JOIN game.dbo.CHAR_INFOR AS g --character data
ON gl1.CHAR_KEY = g.CHAR0_KEY OR gl1.CHAR_KEY = g.CHAR1_KEY OR gl1.CHAR_KEY = g.CHAR2_KEY
JOIN login.dbo.USER_CHECK_LOGIN AS l --login data
ON g.USER_KEY = l.USER_KEY
WHERE l.CHECKLOGIN = 1
ORDER BY username DESC

Sql Server Query Result By Week

I have following tables
Order
ID OrderDate Quantity Item
1 1 jan 2012 5 A
2 6 jan 2012 10 A
3 9 jan 2012 3 B
4 16 jan 2012 2 C
Order Shipped
ID SuppliedOn Quantity Item OrderId
1 7 Jan 2012 3 A 1
2 9 Jan 2012 2 A 1
3 9 Jan 2012 10 A 2
4 17 jan 2012 3 B 3
I want to list order got and order supplied by week
like
Week Total_Order_got Total_Order_Supplied Item
1st week Jan(2 jan 2012) 15 3 A
2nd Week Jan(9 jan 2012) 0 12 A
2nd Week Jan(9 jan 2012) 3 0 B
3rd Week Jan(16 jan 2012) 0 3 B
3rd Week Jan(16 jan 2012) 2 0 C
Here is the solution:
Select my.Mydate, MAX(ThisWeekStart) as ThisWeekStart , SUM(oqty) as oqty,SUM(sqty) as sqty,item from
(Select DatePart(week,o.orderdate) as Mydate,
dateadd(wk, datediff(wk, 0, o.orderdate), 0) as ThisWeekStart,
o.qty as oqty, 0 as sqty, o.item
FROM [order] o
UNION
SELECT DatePart(week,s.suppliedon) as suppliedon,
dateadd(wk, datediff(wk, 0, s.suppliedon), 0) as ThisWeekStart,
0 as oqty, s.qty as sqty, s.item
FROM supply s) as my
GROUP BY my.Mydate, item
Let me know whether it worked for you.

Resources