I am working in SQL Server 2014 and below is my database with which I am working on and need some analysis done on it.
Upon inspecting the database sample carefully we can notice a number 8777 in L9 and in L13 column.
Now I want to get only those columns which have 8777 in them and in the end a column named "count" which shows how many times the number appeared means I need in output something like this as shown below:
So far I have written this query which is giving the category and subcategory correct. But it is showing all the columns. I have no idea how to count the occurrences of a number and show its count in a count column.
select *
from Sheet2$
where '8777' IN ([L1],[L2],[L3],[L4],[L5],[L6],[L7],[L8],[L9],[L10],[L11],[L12],[L13]
To dynamically limit the columns, you would need Dynamic SQL
Example
Select *
Into #Temp
From YourTable A
Unpivot ( Value for Item in ([L1], [L2],[ L3], [L4], [L5], [L6], [L7], [L8], [L9], [L10], [L11], [L12], [L13]) ) u
Where Value = 8777
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(Item) From #Temp Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select *,[Count] = sum(1) over()
From #Temp A
Pivot (max(Value) For [Item] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Category SubCategory L13 L9 Count
C1 SC1 NULL 8777 2
C1 SC3 8777 NULL 2
Hmmm. I think you want the original rows with the count. I think this is:
declare #cols nvarchar(max);
declare #sql nvarchar(max);
set #cols = (select distinct ', ' + v.colname
from t cross apply
(values ('l1', l1),
('l2', l2),
('l3', l3),
('l4', l4),
('l5', l5),
('l6', l6),
('l7', l7),
('l8', l8),
('l9', l9),
('l10', l10),
('l11', l11),
('l12', l12),
('l13', l13)
) v(colname, val)
where v.val = '8777'
for xml path ('')
);
set #sql = '
select category, subcategory' + #cols + ',
count(*) over () as cnt
from t
';
exec sp_executesql #sql;
The only difference from your result is that the count is on every row. That can easily be adjusted using a case expression, but I'm not sure it is necessary.
If you want the count in only one row, then:
set #sql = '
select category, subcategory' + #cols + ',
(case when row_number() over (order by category, subcategory) = 1
then count(*) over ()
end) as cnt
from t
order by category, subcategory
';
You can try this part as a replacement of John's answer's second query to get the proper count, it does not achieve the exact thing you want but can be a work around.
Declare #sql varchar(max) = Stuff((Select Distinct ',' + QuoteName(Item)
From #Temp Order by 1 For XML Path('')),1,1,'')
print #sql;
Select #SQL = '
Select *,value=8777
From #Temp A
Pivot (Count(Value) For [Item] in (' + #sql + ') ) p'
print #sql;
Exec(#SQL);
I just used count function in pivot in place of sum.
Related
i've searched all over for an answer for this, and couldn't really find anything useful,
im pretty much a begginer in Sql so maybe i'm missing something basic
i have a simple table with 2 columns : line, fullname
this is the query i use :
SELECT
(select distributionline from extrasums where key=accounts.accountkey and SuFID='63') as 'line',
fullname
FROM
accounts
ORDER BY
(select distributionline from extrasums where key=accounts.accountkey and SuFID='63'),
(select dotinline from extrasums where key=accounts.accountkey and SuFID='68')
these are the results i get : (not allowed to embed images)
basically i get the distribution lines and the costumers, and the tables is ordered by the dist.lines and the place in line of each costumer.
all i want to do is show these results in a pivot-table style
i tried "pivot" but i understand you cant pivot without aggregate, because thats the whole point of "pivot" in sql.
this is what i want to achieve :
basically the dist.lines are the column names and the results are orders in a pivot way
the dist.lines are not permanent, one day we can have 3 lines. the other we can have 10 lines. its dynamic based on the deliveries for tomorrow. obviously.
same with the costumers.
you're welcome:
DROP TABLE IF EXISTS #temp;
SELECT RANK() OVER (PARTITION BY a.LINE ORDER BY a.fullname) AS rownum, a.line, a.fullname
INTO #temp
FROM ( SELECT 43 AS line, 'Daniel' AS fullname
UNION ALL
SELECT 43 AS line, 'john' AS fullname
UNION ALL
SELECT 43 AS line, 'kenny' AS fullname
UNION ALL
SELECT 43 AS line, 'adam' AS fullname
UNION ALL
SELECT 55 AS line, 'james' AS fullname
UNION ALL
SELECT 55 AS line, 'jones' AS fullname
UNION ALL
SELECT 68 AS line, 'kelly' AS fullname) AS a;
DECLARE #cols AS NVARCHAR(MAX), #colname AS NVARCHAR(MAX), #query AS NVARCHAR(MAX);
SET #colname = STUFF(( SELECT DISTINCT ',' + QUOTENAME(c.line)
FROM #temp AS c
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
, 1
, 1
, '');
SET #cols = STUFF(( SELECT DISTINCT ',ISNULL(' + QUOTENAME(c.line) + ','''') AS '+QUOTENAME(c.line )
FROM #temp AS c
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
, 1
, 1
, '');
PRINT #cols
PRINT #colname
SET #query =
N'SELECT rownum, ' + #cols
+ N' from
(
select rownum
, fullname
, line
from #temp
) x
pivot
(
max(fullname)
for line in (' + #colname + N')
) p ';
PRINT #query
EXECUTE (#query);
enter image description here
I am having trouble sorting a pivot based on a quite large set of data. I have looked at many examples, but none of them seems to address the issue of volume - or perhaps I am just missing something. I have had a very good look here: Sort Columns For Dynamic Pivot and PIVOT in sql 2005 and found much good advise, but I still cannot find the correct way to sort my pivot.
I am using the following sql. It pivots the columns, but the result needs to be sorted for readability:
SELECT a.* INTO #tempA
FROM (SELECT top (5000) id, email, CONVERT(varchar,ROW_NUMBER() OVER
(PARTITION BY email ORDER BY id)) AS PIVOT_CODE FROM Email) a
order by PIVOT_CODE
DECLARE #cols AS NVARCHAR(MAX),
#sql AS NVARCHAR(MAX)
SELECT #cols =STUFF((SELECT DISTINCT ', ' + QUOTENAME(col)
FROM #tempA WITH (NOLOCK)
cross apply
(
SELECT 'id_' + PIVOT_CODE, id
) c (col, so)
group by col, so
--order by col
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #sql = 'SELECT email, '
+#cols+
'INTO ##AnotherPivotTest FROM
(
SELECT email,
col,
value
FROM #tempA WITH (NOLOCK)
cross apply
(
values
(''id_'' + PIVOT_CODE, id)
) c (col, value)
) d
pivot
(
max(value)
for col in ('
+ #cols+
')
) piv'
EXEC (#sql)
SELECT * FROM ##AnotherPivotTest
The result is a chaos to look at:
==============================================================================================
| email | id_19 | id_24 | id_2 | id_16 | id_5 | id_9 | id_23 | .... | id_1 | .... | id_10 |
==============================================================================================
| xx#yy.dk | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 1234 | NULL | NULL |
==============================================================================================
I would very much like the Ids to be sorted - beginning with id_1.
As you can see, I have attempted to place an 'order by' in the selection for 'cols', but that gives me the error: "ORDER BY items must appear in the select list if SELECT DISTINCT is specified." And without DISTINCT, I get another error: "The number of elements in the select list exceeds the maximum allowed number of 4096 elements."
I'm stuck, so any help will be greatly appreciated!
Not sure what causes the problem but I've solved my order problem in my pivot table by inserting the data coming from tempA into another temp table and ordering them there
INSERT INTO #tempB
SELECT * FROM #tempA
ORDER BY PIVOT_CODE
Then selecting distinct ones like so:
SELECT #cols = #cols + QUOTENAME(PIVOT_CODE) + ',' FROM (SELECT DISTINCT PIVOT_CODE FROM #tempB ORDER BY PIVOT_CODE)
SELECT #cols = SUBSTRING(#cols, 0, LEN(#cols)) --trims "," at end
You can also just use a cursor to determine your cols and the order them
Cursor with cols ordered
declare #gruppe nvarchar(max)
declare #gruppeSql nvarchar(max)
declare #SQL nvarchar(max)
DECLARE myCustomers CURSOR FOR
select top 10 FirstName from [dbo].[DimCustomer] Order by FirstName
set #gruppeSql = ''
OPEN myCustomers
FETCH NEXT FROM myCustomers INTO #gruppe
IF (##FETCH_STATUS>=0)
BEGIN
SET #gruppeSql = #gruppeSql +'[' +#gruppe+']'
FETCH NEXT FROM myCustomers INTO #gruppe
END
WHILE (##FETCH_STATUS<>-1)
BEGIN
IF (##FETCH_STATUS<>-2)
SET #gruppeSql = #gruppeSql + ',[' +#gruppe+']'
FETCH NEXT FROM myCustomers INTO #gruppe
END
CLOSE myCustomers
DEALLOCATE myCustomers
SET #gruppeSql = replace(#gruppesql,'''','')
/*Select to preview your cols*/
select #gruppeSql
Dynamic pivot
SET #SQL = '
Select *
from
(
SELECT SalesAmount, FirstName
FROM [AdventureWorksDW2014].[dbo].[FactInternetSales] a inner join dbo.DimCustomer b on a.CustomerKey = b.CustomerKey
) x
pivot
(
sum(SalesAmount)
for FirstName in ('+#gruppesql+')
) p'
print #sql
exec(#sql)
I have an dynamic SQL query which is returning the result as desired but the only problem is the resultant pivot columns are not getting selected in correct order.
I am using SQL Server 2012.
Here is the query:
DECLARE #columns NVARCHAR(MAX), #columns_pivot NVARCHAR(MAX), #sql NVARCHAR(MAX);
SELECT #columns_pivot = COALESCE(#columns_pivot + ', ', '') + QUOTENAME(Week_No)
,#columns = COALESCE(#columns + ', ', '') + 'ISNULL(' + QUOTENAME(Week_No) + ',0) AS ' + QUOTENAME(Week_No) + ''
FROM (SELECT DISTINCT TOP 100 PERCENT DATEPART(wk,T_Date) As Week_No
FROM [VISIT].[dbo].[Report]
WHERE DATEPART(m,T_Date) = 5
ORDER BY Week_No DESC) x; // This query returns Column 'Week_No' in order 22,21,20,19,18 (As Desired)
Edit: //But when Order By Clause is removed it returns 21,18,19,22,20 which is the actual output( Not Desired)
SET #sql = '
SELECT ABC, ' + #columns + '
FROM
(Select TOP 100 PERCENT
ABC
,SUM(CASE WHEN Type = A THEN Sum ELSE 0 END) AS Revenue
,DATEPART(wk,T_Date) As Week_No
FROM [VISIT].[dbo].[Report]
GROUP BY ABC
,DATEPART(wk,T_Date)
ORDER BY Week_No DESC) As j
PIVOT(
max(Revenue)
FOR Week_No in (' + #columns_pivot + ')) As p '
Final result returns columns in the order ABC, 21, 18, 19, 22, 20
But I want the result As ABC, 22, 21, 20, 19, 18.
I saw few posts but was not able to figure out what is wrong with my query. Can someone please point what I have to change in the Query to get the desired output. Where exactly do I have to put the ORDER BY clause
In short when the Select Distinct statement returns the column Week_No in Desc Order why the variables #columns and #columns_pivot don't get it in Desc order
Thanks.
I have solved the issue by storing Week_No in a temp table and apply clustered indexing to it.
Here is the code I added:
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
SET NOCOUNT ON;
DECLARE #params nvarchar(max) = :month
SELECT DISTINCT TOP 100 PERCENT DATEPART(wk,Tour_Datum) As Week_No into #temp
FROM [VISITOUR].[dbo].[BI_001_ADM_Report]
WHERE DATEPART(m,Tour_Datum) = #params
--Clustered index is created on week_no in acending order, so that data is physically stored in that way
CREATE CLUSTERED INDEX cix_wekno ON #temp(Week_No ASC)
I have stuck in a select statement, converting rows into columns. I have tried with PIVOT, i was able to convert the single column. But my requirement is little different. I have explained the requirement below.
I have a table structure as below,
I want to select the data as below,
The values in the table are dynamic, which is not a problem for me to deal with that. But i need a way to get the below result.
Could someone please give me a hint on doing it, may be a way to modify the PIVOT below.
select *
from
(
select TSID,AID,Count,BID
from tbl TS
WHERE TS.TPID = 1
) src
pivot
(
sum(Count)
for AID in (AID1,AID2,AID3)
) piv
Thank you..
You may check this fiddle
EDIT
This will work for not previously known column names
DECLARE #Columns AS VARCHAR(MAX)
DECLARE #SQL AS VARCHAR(MAX)
SELECT #Columns = STUFF(( SELECT DISTINCT ',' + AID
FROM Table1
FOR
XML PATH('')
), 1, 1, '')
SET #SQL = '
;WITH MyCTE AS
(
SELECT TSID,
AID,
STUFF(( SELECT '','' + CONVERT(VARCHAR,[Count] )
FROM Table1 I Where I.TSID = O.TSID
FOR
XML PATH('''')
), 1, 1, '''') AS CountList
FROM Table1 O
GROUP BY TSID,
AID
)
SELECT *
FROM MyCTE
PIVOT
(
MAX(CountList)
FOR AID IN
(
' + #Columns + '
)
) AS PivotTable'
EXEC(#SQL)
This is probably simple but I"m just not seeing it. Your help is appreciated. I have a table in MS SQLServer that looks like this
CustomerID Time ItemID
1 2008-10-07 06:32:53:00.000 87432
1 2008-10-07 06:32:53:00.000 26413
2 2010-06-23 03:45:10:00.000 6312
2 2011-09-14 07:36:03:00.000 87432
2 2011-09-14 07:36:03:00.000 87432
I want to end up with a table that has each customer, the timestamp and the count of the items purchased during that timestamp, that looks like this
CustomerID Time 87432 26413 6312
1 2008-10-07 06:32:53:00.000 1 1 0
2 2010-06-23 03:45:10:00.000 0 0 1
2 2011-09-14 07:36:03:00.000 2 0 0
In the source table, the time and itemID are variable (and plentiful), so I'm thinking a dynamic pivot will do the trick. Is this possible to do with pivot? If so, how?
You can do this with a dynamic PIVOT. This will count the number of ItemIds that you have for any number of Times.
See a SQL Fiddle with a Demo. This demo leaves the time as a varchar as you stated they were. But this will work if the data is a datetime as well.
Since you want time in the final result, then when you select the columns, you will need to add the time column twice. I called it time1 and time. This allows you to aggregate on time1 in the PIVOT and still have a time column for your final product.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(itemid)
from temp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT customerid, [time], ' + #cols + ' from
(
select customerid, [time] as time1, [time] as [time], itemid
from temp
) x
pivot
(
count([time1])
for itemid in (' + #cols + ')
) p '
execute(#query)
Approach #1 - Click here to see Demo
Declare #ItemIDs varchar(1000) = ''
Declare #Query varchar(8000) = ''
Select #ItemIDs = ISNULL(QuoteName(Convert(varchar, ItemID)) + ',', '')
+ #ItemIDs
From
(
Select distinct ItemID From #MyTable
)K
SET #ItemIDs = SUBSTRING(#ItemIDs,0,len(#ItemIDs))
SET #Query = 'Select CustomerID, [Time],' +
#ItemIDs + ' From
(
Select CustomerID, [Time], ItemID from #MyTable
)K Pivot
(
count(ItemID) FOR ItemID IN (' + #ItemIDs + ')
) AS pvt'
EXEC(#Query)
Approach #2 - Click here to see Demo
Select CustomerID, [Time], [87432] as [87432],
[26413] as [26413], [6312] as [6312] From
(
Select CustomerID, [Time], ItemID from #MyTable
)K Pivot
(
count(ItemID) FOR ItemID IN ([87432] , [26413],[6312])
) AS pvt