I want to make all the NULL values produced by the pivot to become 0s. I have placed ISNULL in every place imaginable, but does not seem to have any effect. Are pivots compatible with ISNULL? Code below:
DECLARE #startDate datetime
SET #startDate = '2013-01-01'
DECLARE #sql varchar(MAX)
SET #sql = 'SELECT
CLIENTNAME, PROJECTNAME, RESOURCE, [' +
REPLACE(SUBSTRING(CONVERT(varchar, #startDate, 13), 4, 8), ' ', '') + '], [' +
REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 1, #startDate), 13), 4, 8), ' ', '') + '], [' +
REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 2, #startDate), 13), 4, 8), ' ', '') + '], [' +
REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 3, #startDate), 13), 4, 8), ' ', '') + '], [' +
REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 4, #startDate), 13), 4, 8), ' ', '') + '], [' +
REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 5, #startDate), 13), 4, 8), ' ', '') + ']
FROM
(
SELECT
CLIENTNAME, PROJECTNAME, RESOURCE, FORECASTTOTAL
FROM viewprojscheduling_group
) AS SourceTable
PIVOT
(
SUM(FORECASTTOTAL)
FOR SCHEDULEDDATE IN (' +
QUOTENAME(REPLACE(SUBSTRING(CONVERT(varchar, #startDate, 13), 4, 8), ' ', '')) + ', ' +
QUOTENAME(REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 1, #startDate), 13), 4, 8), ' ', '')) + ', ' +
QUOTENAME(REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 2, #startDate), 13), 4, 8), ' ', '')) + ', ' +
QUOTENAME(REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 3, #startDate), 13), 4, 8), ' ', '')) + ', ' +
QUOTENAME(REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 4, #startDate), 13), 4, 8), ' ', '')) + ', ' +
QUOTENAME(REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 5, #startDate), 13), 4, 8), ' ', '')) + ')
) AS PivotTable'
execute(#sql)
I would set your query up slightly different because while it is dynamic in that the column names are changing, you have still hard-coded the number of columns.
First, I would use a recursive CTE to generate the list of months/years that you want to create.
DECLARE #startDate datetime
SET #startDate = '2013-01-01'
;with dates as
(
select #startdate datelist, 1 sp
union all
select dateadd(month, 1, datelist), sp+1
from dates
where sp+1 <= 5 -- change this number 5 to the number of months you need
)
select sp,
REPLACE(SUBSTRING(CONVERT(varchar(11), datelist, 13), 4, 8), ' ', '') MONTHANDYEAR
from dates
See SQL Fiddle with Demo. This is going to create your list of the 5 months with the year automatically. Then you are not hard-coding the 5 columns. Your current query is not as flexible as it could be. What will happen if you then want 12 months, you are going to have to change your code.
Once you generate the list of dates, I would insert it into a temp table so you can use it to get the columns.
The code to get the list of columns is:
select #cols = STUFF((SELECT ',' + QUOTENAME(monthandyear)
from #datesTemp
group by monthandyear, sp
order by sp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colNames = STUFF((SELECT ', isnull(' + QUOTENAME(monthandyear)+', 0) as '+QUOTENAME(monthandyear)
from #datesTemp
group by monthandyear, sp
order by sp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
See SQL Fiddle with Demo. You will see that there are two versions. The first one #cols gets the list of columns that will be used in the pivot. The second #colNames will be used in the final SELECT list to replace the null values with the zeros.
Then you put it all together and the code will be: (Note: I am using a version of my answer from your previous question)
DECLARE #cols AS NVARCHAR(MAX),
#colNames AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#startDate datetime
SET #startDate = '2013-01-01'
;with dates as
(
select #startdate datelist, 1 sp
union all
select dateadd(month, 1, datelist), sp+1
from dates
where sp+1 <= 5 -- change this number 5 to the number of months you need
)
select sp,
REPLACE(SUBSTRING(CONVERT(varchar(11), datelist, 13), 4, 8), ' ', '') MONTHANDYEAR
into #datesTemp
from dates
select #cols = STUFF((SELECT ',' + QUOTENAME(monthandyear)
from #datesTemp
group by monthandyear, sp
order by sp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colNames = STUFF((SELECT ', isnull(' + QUOTENAME(monthandyear)+', 0) as '+QUOTENAME(monthandyear)
from #datesTemp
group by monthandyear, sp
order by sp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT resource, clientname,' + #colNames + '
from
(
select [CLIENTNAME], [RESOURCE], [FORECASTTOTAL],
REPLACE(SUBSTRING(CONVERT(varchar(11), SCHEDULEDDATE, 13), 4, 8), '' '', '''') monthandyear
from viewprojscheduling_group
) x
pivot
(
sum(FORECASTTOTAL)
for monthandyear in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo. This query will give you the result:
| RESOURCE | CLIENTNAME | JAN2013 | FEB2013 | MAR2013 | APR2013 | MAY2013 |
---------------------------------------------------------------------------
| res1 | abc | 1000 | 2000 | 0 | 0 | 0 |
| res1 | def | 0 | 0 | 2000 | 0 | 0 |
| res2 | def | 1500 | 0 | 0 | 0 | 0 |
| res3 | ghi | 0 | 0 | 2500 | 0 | 0 |
Related
I have problem to switch rows to columns and vice versa in SQL Server 2008, I have tried any queries to a solution but I did not get a proper results.
I've a table as following:
declare #tmpTable table
(name varchar(20), date_ date, sales_code char(1), sales smallint, earned int)
insert into #tmpTable
values ('Robert', '2016/8/1', 'A', 2, 30),
('Robert', '2016/8/1', 'B', 3, 45),
('Robert', '2016/8/2', 'B', 1, 15),
('Robert', '2016/8/3', 'B', 2, 30),
('Jhon', '2016/8/1', 'A', 3, 45),
('Jhon', '2016/8/2', 'A', 3, 45),
('Jhon', '2016/8/3', 'B', 2, 30)
select * from #tmpTable;
Result:
Name date_ sales_code sales earned
------ ---------- ---------- ----- ------
Robert 2016-08-01 A 2 30
Robert 2016-08-01 B 3 45
Robert 2016-08-02 B 1 15
Robert 2016-08-03 B 2 30
Jhon 2016-08-01 A 3 45
Jhon 2016-08-02 A 3 45
Jhon 2016-08-03 B 2 30
Then, I have next query :
select * from (
select name, 'sales_code' as category, date_, sales_code from (
select * from (
SELECT name, date_
,STUFF((SELECT ', ' + sales_code [text()]
FROM #tmpTable
WHERE date_ = t.date_
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') sales_code
,STUFF((SELECT ', ' + convert(varchar(max), sales) [text()]
FROM #tmpTable
WHERE date_ = t.date_
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') sales
,STUFF((SELECT ', ' + convert(varchar(max), earned) [text()]
FROM #tmpTable
WHERE date_ = t.date_
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') earned
FROM #tmpTable t
GROUP BY name, date_
) as a
) as a
) as a
pivot (
max(sales_code) FOR date_ IN ([2016/8/1], [2016/8/2], [2016/8/3])
)as pv
union all
select * from (
select name, 'sales' as category, date_, sales from (
select * from (
SELECT name, date_
,STUFF((SELECT ', ' + sales_code [text()]
FROM #tmpTable
WHERE date_ = t.date_
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') sales_code
,STUFF((SELECT ', ' + convert(varchar(max), sales) [text()]
FROM #tmpTable
WHERE date_ = t.date_
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') sales
,STUFF((SELECT ', ' + convert(varchar(max), earned) [text()]
FROM #tmpTable
WHERE date_ = t.date_
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') earned
FROM #tmpTable t
GROUP BY name, date_
) as a
) as a
) as a
pivot (
max(sales) FOR date_ IN ([2016/8/1], [2016/8/2], [2016/8/3])
)as pv
union all
select * from (
select name, 'earned' as category, date_, earned from (
select * from (
SELECT name, date_
,STUFF((SELECT ', ' + sales_code [text()]
FROM #tmpTable
WHERE date_ = t.date_
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') sales_code
,STUFF((SELECT ', ' + convert(varchar(max), sales) [text()]
FROM #tmpTable
WHERE date_ = t.date_
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') sales
,STUFF((SELECT ', ' + convert(varchar(max), earned) [text()]
FROM #tmpTable
WHERE date_ = t.date_
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') earned
FROM #tmpTable t
GROUP BY name, date_
) as a
) as a
) as a
pivot (
max(earned) FOR date_ IN ([2016/8/1], [2016/8/2], [2016/8/3])
)as pv
It will display the result:
name category 2016/8/1 2016/8/2 2016/8/3
------- -------- -------- ------- --------
Jhon sales_code A, B, A B, A B, B
Robert sales_code A, B, A B, A B, B
Jhon sales 2, 3, 3 1, 3 2, 2
Robert sales 2, 3, 3 1, 3 2, 2
Jhon earned 30, 45, 45 15, 45 30, 30
Robert earned 30, 45, 45 15, 45 30, 30
But, I would like to get the following result:
name category 2016/8/1 2016/8/2 2016/8/3
---- -------- -------- -------- --------
Robert sales_code A, B B B
Robert sales 2, 3 1 2
Robert earned 30, 45 15 30
Jhon sales_code A A B
Jhon sales 3 3 2
Jhon earned 45 45 30
Thanks a lot for any help.
first you need to unpivot your data.. to do this, all data types much match, so you need to convert the 2 numeric columns to varchars.
still use stuff before you unpivot to get the combined values per name, date_ but use distinct to only get the name, date_ values once.
after you unpivot, you just need to pivot again.
SELECT *
FROM ( SELECT DISTINCT
Name,
date_,
sales_code = STUFF((SELECT ', ' + sales_code
FROM #tmpTable t2
WHERE t2.Name = t.Name AND t2.date_ = t.date_
FOR XML PATH('')), 1, 2, ''),
sales = STUFF((SELECT ', ' + CONVERT(VARCHAR, sales)
FROM #tmpTable t2
WHERE t2.Name = t.Name AND t2.date_ = t.date_
FOR XML PATH('')), 1, 2, ''),
earned = STUFF((SELECT ', ' + CONVERT(VARCHAR, earned)
FROM #tmpTable t2
WHERE t2.Name = t.Name AND t2.date_ = t.date_
FOR XML PATH('')), 1, 2, '')
FROM #tmpTable t) t
UNPIVOT (
val
FOR category IN (sales_code, sales, earned)
) up
PIVOT (
MAX(val)
FOR date_ IN ([2016-08-01], [2016-08-02], [2016-08-03])
) p
ORDER BY name DESC,
category DESC
I'm using SQL Server 2014 and i nee some help with a hard query.
I have the following table (MyTable). These columns names are just for the example. They are actually totally different from each other.
id int,
col1 int,
col2 int,
..
..
..
col70 int
For each pairs of sequential columns {(col1, col2), (col2_col3)...(col69_col70)}, i need to calculate the following: The number of different pairs that each values has - col_i is the static column, and col_i+1 is the other one. Each value need to be divided by the total amount of records in the table. For example:
col1 | col2
45 | 789
56 | 345
99 | 234
45 | 789
45 | 222
89 | 678
89 | 345
45 | 789
90 | 234
12 | 567
Calculation:
((45, 789)+(45, 222))/10
(56, 345)/10
(99, 234)/10
(45, 789)+(45, 222)/10
(45, 789)+(45, 222)/10
(89, 678)+(89, 345)/10
(89, 678)+(89, 345)/10
((45, 789)+(45, 222))/10
(90, 234)/10
(12, 567)/10
Output:
col1_col2
0.2
0.1
0.1
0.2
0.2
0.2
0.2
0.2
0.1
0.1
Explanation for the first records:
45 is the value of the static column ,so now i'll check how many different combination we can find with col2:
45 | 789
45 | 789
45 | 222
45 | 789
Total distinct combinations divided by number of records in the table: 2/10 = 0.2
This calculation need for each pairs of sequential columns. Any recommendation? Is there's a smart way to calculate it automatically instead of writing a query with line for each pair?
An example assuming you have a primary key:
create table my_table
(column_id int not null,
column1 int not null,
column2 int not null);
insert into my_table
(column_id, column1, column2)
values
(1, 45,789),
(2, 56,345),
(3, 99,234),
(4, 45,789),
(5, 45,222),
(6, 89,678),
(7, 89,345),
(8, 45,789),
(9, 90,234),
(10, 12,567);
declare #column_a as nvarchar(100) = N'column1';
declare #column_b as nvarchar(100) = N'column2';
declare #result_column as nvarchar(100) = N'column1_2';
declare #sql_string as nvarchar(4000)
set #sql_string =
'select a.column_id,
1.0 * count( distinct b.' + #column_b + ') / (count(a.' + #column_a + ') over ()) as ' + #result_column
+ ' from my_table a
inner join my_table b
on a.' + #column_a + ' = b.' + #column_a +
' group by a.column_id, a.' + #column_a +
' order by a.column_id';
-- print #sql_string;
execute(#sql_string);
If there's no primary key you could use the rownumber() function to create an identifier, but the result order would change. The print command can be useful for checking the dynamic sql string, here commented out.
Putting the dynamic SQL into a stored procedure:
create procedure column_freq #column_a nvarchar(100), #column_b nvarchar(100), #result_column nvarchar(100)
as
begin
declare #sql_string as nvarchar(4000);
set #sql_string =
'select a.column_id,
1.0 * count( distinct b.' + #column_b + ') / (count(a.' + #column_a + ') over ()) as ' + #result_column
+ ' from my_table a
inner join my_table b
on a.' + #column_a + ' = b.' + #column_a +
' group by a.column_id, a.' + #column_a +
' order by a.column_id';
execute(#sql_string);
end;
go
exec column_freq N'column1', N'column2', N'column1_2';
go
Is it possible to add ellipsis after the MySQL substring_index function?
So if I have the following code:
SET #string = "abc, def, ghi, jkl, mno";
SELECT SUBSTRING_INDEX(#string, ', ', 3);
The result is: abc, def, ghi
So is it possible to add ... at the back ONLY when it is cut?
I wish to retrieve this:
SELECT SUBSTRING_INDEX(#string, ', ', 3);
---> RESULT: abc, def, ghi...
SELECT SUBSTRING_INDEX(#string, ', ', 5);
---> RESULT: abc, def, ghi, jkl, mno
You need some conditional logic:
select (case when #string = substring_index(#string, ', ', 3)
then substring_index(#string, ', ', 3)
else concat(substring_index(#string, ', ', 3), '...')
end)
I have table column that contains in each row data like this:
| Simbols |
|--------------------------------------|
|H412 Text text |
|H413 Text text text text |
|EUH 001 Text text text text text text |
|EUH 006 text text |
|EUH 201/201A Text text. Text text |
And I need from that data get data like this:
|Simbols |
|------------|
|H412 |
|H413 |
|EUH 001 |
|EUH 006 |
|EUH 201/201A|
I tried with SUBSTRING and CHARINDEX but it till the end don't work... It takes only first space or something like that...
QUERY:
SELECT
CASE
WHEN SUBSTRING(Simbols, 1, CHARINDEX(' ', Simbols)) = ''
THEN Simbols + ' '
ELSE SUBSTRING(Simbols, 1, CHARINDEX(' ', Simbols))
END 'Simbols'
FROM dbo.table
RESULT:
| Simbols |
|------------|
|H412 |
|H413 |
|EUH |
|EUH |
|EUH |
How can I make this work, and where is the problem?
Maybe there is different way to get these Simbols?
P.S. "Text text text" is a example, there comes a explanations of "Simbols"
The CharIndex() function has an optional 3rd parameter - start_location - that will be key here.
SELECT your_column
, CharIndex(' ', your_column) As first_space
, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1) As second_space
, SubString(your_column, 1, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1)) As first_two_words
FROM your_table
Unfortunately when the CharIndex() function can't find the specified string (in this case a single space ' ') it will return 0 (zero).
This means that if there isn't a first or second space the result of first_two_words in my above example will return an empty string as SubString(your_column, 1, 0) = ''.
To get around this you need to be a little clever.
Essentially, if second_space = 0 then we need to return the full string. We have a few options for this:
SELECT your_column
, CharIndex(' ', your_column) As first_space
, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1) As second_space
, SubString(your_column, 1, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1)) As first_two_words
, SubString(your_column, 1, Coalesce(NullIf(CharIndex(' ', your_column, CharIndex(' ', your_column) + 1), 0), Len(your_column))) As first_two_words_option1
, CASE WHEN CharIndex(' ', your_column, CharIndex(' ', your_column) + 1) = 0 THEN your_column ELSE SubString(your_column, 1, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1)) END As first_two_words_option2
FROM (
SELECT 'one' As your_column
UNION ALL SELECT 'one two'
UNION ALL SELECT 'one two three'
UNION ALL SELECT 'one two three four'
) As x
Try this: It works
SELECT CASE WHEN charindex(' ', Simbols, charindex(' ', Simbols) + 1) = 0
THEN Simbols
ELSE LEFT(Simbols, charindex(' ', Simbols, charindex(' ', Simbols) + 1))
END
FROM dbo.table
Here is screenshot what I tried.
Here is new EDIT
SELECT REPLACE(Simbols, 'text', '') FROM dbo.table
Here is screen shot
Try something like this:
select TRIM(REPLACE(lower(type),"text" ,"")) as T, type from supportContacts
Sql Fiddle: http://sqlfiddle.com/#!2/d5cf8/4
for more info :http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_replace
I have a table set up like below:
CLIENTNAME MONTHANDYEAR RESOURCE COST
abc JAN2011 res1 1000
abc FEB2011 res1 2000
def JAN2011 res2 1500
def MAR2011 res1 2000
ghi MAR2011 res3 2500
I need an output like below. Months are to be generated dynamically in 3-month intervals. In this case, is there a way to pivot by MONTHANDYEAR as well as group by clientname?
RESOURCE CLIENTNAME JAN2011 FEB2011 MAR2011
res1 abc 1000 1000
res1 def 2000
res2 def 1500
res3 ghi 2500
This is what the PIVOT operator is for:
SELECT
Resource, ClientName,
[JAN2011], [FEB2011], [MAR2011]
FROM
(
SELECT
*
FROM tblname
) AS SourceTable
PIVOT
(
SUM(COST)
FOR MONTHANDYEAR IN ([JAN2011], [FEB2011], [MAR2011])
) AS PivotTable;
Since your months are selected dynamically using #startDate as a base month, you can use the following dynamic query:
DECLARE #startDate datetime
SET #startDate = '2011-01-01'
DECLARE #sql varchar(MAX)
SET #sql = 'SELECT
Resource, ClientName, [' +
REPLACE(SUBSTRING(CONVERT(varchar, #startDate, 13), 4, 8), ' ', '') + '], [' +
REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 1, #startDate), 13), 4, 8), ' ', '') + '], [' +
REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 2, #startDate), 13), 4, 8), ' ', '') + ']
FROM
(
SELECT
*
FROM tblName
) AS SourceTable
PIVOT
(
SUM(COST)
FOR MONTHANDYEAR IN (' +
QUOTENAME(REPLACE(SUBSTRING(CONVERT(varchar, #startDate, 13), 4, 8), ' ', '')) + ', ' +
QUOTENAME(REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 1, #startDate), 13), 4, 8), ' ', '')) + ', ' +
QUOTENAME(REPLACE(SUBSTRING(CONVERT(varchar, DATEADD(MONTH, 2, #startDate), 13), 4, 8), ' ', '')) + ')
) AS PivotTable'
execute(#sql)
working sqlfiddle here
This data transformation can be done with the PIVOT function.
If you know the values, then you can hard-code the monthandyear dates:
select resource,
clientname,
isnull(jan2011, '') Jan2011,
isnull(feb2011, '') Feb2011,
isnull(mar2011, '') Mar2011
from
(
select clientname, monthandyear, resource, cost
from yourtable
) src
pivot
(
sum(cost)
for monthandyear in (Jan2011, Feb2011, Mar2011)
) piv;
See SQL Fiddle with Demo.
But if the dates are unknown, then you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#colNames AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(monthandyear)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colNames = STUFF((SELECT distinct ', isnull(' + QUOTENAME(monthandyear)+', 0) as '+QUOTENAME(monthandyear)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT resource, clientname,' + #colNames + ' from
(
select clientname, monthandyear, resource, cost
from yourtable
) x
pivot
(
sum(cost)
for monthandyear in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo.
The result of both is:
| RESOURCE | CLIENTNAME | JAN2011 | FEB2011 | MAR2011 |
-------------------------------------------------------
| res1 | abc | 1000 | 2000 | 0 |
| res1 | def | 0 | 0 | 2000 |
| res2 | def | 1500 | 0 | 0 |
| res3 | ghi | 0 | 0 | 2500 |
SELECT Resource, Clientname
, SUM(CASE WHEN MonthAndYear = 'JAN2011' THEN COST ELSE 0 END) AS JAN2011
, SUM(CASE WHEN MonthAndYear = 'FEB2011' THEN COST ELSE 0 END) AS FEB2011
, SUM(CASE WHEN MonthAndYear = 'MAR2011' THEN COST ELSE 0 END) AS MAR2011
FROM yourtable
GROUP BY Resource, Clientname
You can also remove the ELSE 0 to return a NULL value for resource/clientname combinations without data