MsSql "group by" result table design - sql

How to convert Result 1 to result 2. I can not get result2 using the pivot table.
Result1
Result1 QUERY
SELECT Country,City,Count(*) as "Count"
FROM Customers
GROUP BY Country,City
Result2

There is no such feature of SQL other that PIVOT where, as you have discovered, you need to know the values up front.
This formatting is typically done in the output part of the system, such as your report engine. SSRS for example can handle this nicely & out of the box.
You would need dynamic SQL to achieve this lower down in the stack.

Try this dynamic sql
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
CREATE TABLE #Temp([Country] VARCHAR(20),[City] VARCHAR(20),[Count] INT)
INSERT INTO #Temp
SELECT 'Germany' ,'Aachen' ,1 UNION ALL
SELECT 'USA' ,'Albuquerque',1 UNION ALL
SELECT 'USA' ,'Anchorage' ,1 UNION ALL
SELECT 'Denmark' ,'Arhus' ,1 UNION ALL
SELECT 'Spain' ,'Barcelone' ,1
DECLARE #Colmn nvarchar(max),
#Sql nvarchar(max)
SELECT #Colmn=STUFF((SELECT distinct ', '+QUOTENAME([Country]) FROM #Temp
FOR XML PATH ('')),1,1,'')
SET #Sql =' SELECT Cityynames ,'+ #Colmn +' FROM
(
SELECT *,Country AS Countrynames,City AS Cityynames
FROM #Temp
)dt
PIVOT
(
MAX([Count]) FOR [Country] IN ('+#Colmn+')
) AS Pvt'
PRINT #Sql
EXEC (#Sql)

Related

How to write vertical columns [duplicate]

This question already has answers here:
Understanding PIVOT function in T-SQL
(7 answers)
Closed 3 years ago.
How can I write a vertical table horizontally with sql
  I want to make the following table like the example in the second picture
example
ı want to write
Try this
DECLARE #Sql nvarchar(max),
#DynamicColumn nvarchar(max),
#MaxDynamicColumn nvarchar(max)
SELECT #DynamicColumn = STUFF((SELECT DISTINCT', '+QUOTENAME(CAST(Col1 AS VARCHAR(50)))
FROM #Temp FOR XML PATH ('')),1,1,'')
SELECT #DynamicColumn
SET #Sql='SELECT '+ #DynamicColumn+'
FROM
(
SELECT *
FROM #Temp o
)AS src
PIVOT
(
MAX(Col2) FOR [Col1] IN ('+#DynamicColumn+')
) AS Pvt
'
EXEC (#Sql)
PRINT #Sql
Hi if understand your query i think this query can help you to get excepted result :
CREATE TABLE #TEMP (colName varchar(250), colOther varchar(250))
INSERT INTO #TEMP
SELECT 'Desen', '2908A' UNION ALL
SELECT 'Desen', '2908A' UNION ALL
SELECT 'Desen', '2908A' UNION ALL
SELECT 'Desen', '2908A' UNION ALL
SELECT 'Ebat', '125x200 R' UNION ALL
SELECT 'Ebat', '125x200 R' UNION ALL
SELECT 'Ebat', '125x200 R' UNION ALL
SELECT 'Ebat', '125x200 R' UNION ALL
SELECT 'ZeminRengi', 'KEMIK' UNION ALL
SELECT 'ZeminRengi', 'KEMIK' UNION ALL
SELECT 'ZeminRengi', 'KEMIK' UNION ALL
SELECT 'ZeminRengi', 'KEMIK'
select Desen,Ebat,ZeminRengi
from #TEMP
PIVOT (
MAX(colOther)
FOR colName IN (Desen,Ebat,ZeminRengi)) AS Pvt
DROP TABLE #TEMP
See different link and google :
Understanding PIVOT function in T-SQL
MSDN
Google- search

Need help on Pivot in SQL Server

Need help on pivot.. My i/p and o/p as below(pls see the attached image)..Could u pls help me on the query...
Here created sample data for getting result
IF OBJECT_ID('Tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
;WIth cte(EmployeeID, Activity_Date,Activity_Type)
AS
(
SELECT 1,'6/20/2016' ,'Mail_send' UNION ALL
SELECT 2,'6/22/2011' ,'Mail_Received' UNION ALL
SELECT 1,'10/11/2016','Mail_Replied' UNION ALL
SELECT 3,'10/31/2016','Mail_deleted' UNION ALL
SELECT 2,'2/11/2016' ,'Mail_Forwared'
)
SELECT * INTO #Temp FROM cte
We can get the result by using dynamic Sql with Pivot
DECLARE #dynamicCol nvarchar(max),
#Sql nvarchar(max)
SELECT #dynamicCol=STUFF((SELECT DISTINCT ', ' +QUOTENAME(Activity_Type) FROM #Temp
FOR XML PATH('')),1,1,'')
SET #Sql='
SELECT [EmployeeID] , '+ #dynamicCol +' From
(
SELECT * From
#temp
)AS Src
PIVOT
(
MAX([Activity_Date]) For [Activity_Type] IN ('+#dynamicCol+')
)
AS Pvt
'
PRINT #Sql
EXEC(#Sql)
Result
EmployeeID Mail_deleted Mail_Forwared Mail_Received Mail_Replied Mail_send
--------------------------------------------------------------------------------------
1 NULL NULL NULL 10/11/2016 6/20/2016
2 NULL 2/11/2016 6/22/2011 NULL NULL
3 10/31/2016 NULL NULL NULL NULL
Try the following Query, this should serve your purpose:
select * from
(select [Employee Id],Activity_Date,Activity_Type from Employee_Activity) tbl
pivot
(
max(Activity_Date) for Activity_Type in (Mail_Send,Mail_Received,Mail_Replied,Mail_deleted,Mail_Forwarded)
)as pvt

Can I pivot dynamic table with no group by in SQL Server

This is my data in table (left join from field table and value table)
This is my expected result table after use pivot function
Thanks for help ^___^
I suggest you to use dynamic SQL as fieldnames number may very in future:
DECLARE #columns nvarchar(max),
#sql nvarchar(max)
SELECT #columns = COALESCE(#columns,'') + ',' + QUOTENAME(c.fieldname)
FROM [Columns] c
ORDER BY c.cid
SELECT #sql = N'
SELECT *
FROM (
SELECT v.[row],
c.fieldname,
v.fieldvalue
FROM [Values] v
INNER JOIN [Columns] c
ON v.cid = c.cid
) t
PIVOT (
MAX(fieldvalue) FOR fieldname IN ('+STUFF(#columns,1,1,'')+')
) pvt'
EXEC sp_executesql #sql
Will output:
row FirstName LastName Email Phone
1 Arnun Saelim Arnun.s#outlook.com 0922743838
2 Micheal Saelim Micheal#gmail.com 0886195353
I'm not sure why group by got into your mind, but here is a working example of what you are trying to achieve.
select * into #temp from
( values
(1,'A','AV'),
(1,'B','BV'),
(1,'C','CV'),
(2,'A','AV'),
(2,'B','BV'),
(2,'C','CV'))
as t(row, FieldName, FieldValue)
select *
from #temp
PIVOT
(MAX(FieldValue) FOR FieldName in ([A],[B],[C])) as pvt
Does that work for you?

Dynamic SELECT statement, generate columns based on present and future values

Currently building a SELECT statement in SQL Server 2008 but would like to make this SELECT statement dynamic, so the columns can be defined based on values in a table. I heard about pivot table and cursors, but seems kind of hard to understand at my current level, here is the code;
DECLARE #date DATE = null
IF #date is null
set # date = GETDATE() as DATE
SELECT
Name,
value1,
value2,
value3,
value4
FROM ref_Table a
FULL OUTER JOIN (
SELECT
PK_ID ID,
sum(case when FK_ContainerType_ID = 1 then 1 else null) Box,
sum(case when FK_ContainerType_ID = 2 then 1 else null) Pallet,
sum(case when FK_ContainerType_ID = 3 then 1 else null) Bag,
sum(case when FK_ContainerType_ID = 4 then 1 else null) Drum
from
Packages
WHERE
#date between PackageStart AND PackageEnd
group by PK_ID ) b on a.Name = b.ID
where
Group = 0
The following works great for me , but PK_Type_ID and the name of the column(PackageNameX,..) are hard coded, I need to be dynamic and it can build itself based on present or futures values in the Package table.
Any help or guidance on the right direction would be greatly appreciated...,
As requested
ref_Table (PK_ID, Name)
1, John
2, Mary
3, Albert
4, Jane
Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
1 , 1, 4, 1JAN2014, 30JAN2014
2 , 2, 3, 1JAN2014, 30JAN2014
3 , 3, 2, 1JAN2014, 30JAN2014
4 , 4, 1, 1JAN2014, 30JAN2014
ContainerType (PK_ID, Type)
1, Box
2, Pallet
3, Bag
4, Drum
and the result should look like this;
Name Box Pallet Bag Drum
---------------------------------------
John 1
Mary 1
Albert 1
Jane 1
The following code like I said works great, the issue is the Container table is going to grow and I need to replicated the same report without hard coding the columns.
What you need to build is called a dynamic pivot. There are plenty of good references on Stack if you search out that term.
Here is a solution to your scenario:
IF OBJECT_ID('tempdb..##ref_Table') IS NOT NULL
DROP TABLE ##ref_Table
IF OBJECT_ID('tempdb..##Packages') IS NOT NULL
DROP TABLE ##Packages
IF OBJECT_ID('tempdb..##ContainerType') IS NOT NULL
DROP TABLE ##ContainerType
SET NOCOUNT ON
CREATE TABLE ##ref_Table (PK_ID INT, NAME NVARCHAR(50))
CREATE TABLE ##Packages (PK_ID INT, FK_ref_Table_ID INT, FK_ContainerType_ID INT, PackageStartDate DATE, PackageEndDate DATE)
CREATE TABLE ##ContainerType (PK_ID INT, [Type] NVARCHAR(50))
INSERT INTO ##ref_Table (PK_ID,NAME)
SELECT 1,'John' UNION
SELECT 2,'Mary' UNION
SELECT 3,'Albert' UNION
SELECT 4,'Jane'
INSERT INTO ##Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
SELECT 1,1,4,'2014-01-01','2014-01-30' UNION
SELECT 2,2,3,'2014-01-01','2014-01-30' UNION
SELECT 3,3,2,'2014-01-01','2014-01-30' UNION
SELECT 4,4,1,'2014-01-01','2014-01-30'
INSERT INTO ##ContainerType (PK_ID, [Type])
SELECT 1,'Box' UNION
SELECT 2,'Pallet' UNION
SELECT 3,'Bag' UNION
SELECT 4,'Drum'
DECLARE #DATE DATE, #PARAMDEF NVARCHAR(MAX), #COLS NVARCHAR(MAX), #SQL NVARCHAR(MAX)
SET #DATE = '2014-01-15'
SET #COLS = STUFF((SELECT DISTINCT ',' + QUOTENAME(T.[Type])
FROM ##ContainerType T
FOR XML PATH, TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #SQL = 'SELECT [Name], ' + #COLS + '
FROM (SELECT [Name], [Type], 1 AS Value
FROM ##ref_Table R
JOIN ##Packages P ON R.PK_ID = P.FK_ref_Table_ID
JOIN ##ContainerType T ON P.FK_ContainerType_ID = T.PK_ID
WHERE #DATE BETWEEN P.PackageStartDate AND P.PackageEndDate) X
PIVOT (COUNT(Value) FOR [Type] IN (' + #COLS + ')) P
'
PRINT #COLS
PRINT #SQL
SET #PARAMDEF = '#DATE DATE'
EXEC SP_EXECUTESQL #SQL, #PARAMDEF, #DATE=#DATE
Output:
Name Bag Box Drum Pallet
Albert 0 0 0 1
Jane 0 1 0 0
John 0 0 1 0
Mary 1 0 0 0
Static Query:
SELECT [Name],[Box],[Pallet],[Bag],[Drum] FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( [Box],[Pallet],[Bag],[Drum])
) AS PivotTable
) AS Main
ORDER BY RFID
Dynamic Query:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + [Type] + ']'
FROM ContanerType
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT [Name],' + #columnList + ' FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( ' + #columnList + ')
) AS PivotTable
) AS Main
ORDER BY RFID;'
EXEC sp_executesql #pivotsql
Following my tutorial below will help you to understand the PIVOT functionality:
We write sql queries in order to get different result sets like full, partial, calculated, grouped, sorted etc from the database tables. However sometimes we have requirements that we have to rotate our tables. Sounds confusing?
Let's keep it simple and consider the following two screen grabs.
SQL Table:
Expected Results:
Wow, that's look like a lot of work! That is a combination of tricky sql, temporary tables, loops, aggregation......, blah blah blah
Don't worry let's keep it simple, stupid(KISS).
MS SQL Server 2005 and above has a function called PIVOT. It s very simple to use and powerful. With the help of this function we will be able to rotate sql tables and result sets.
Simple steps to make it happen:
Identify all the columns those will be part of the desired result set.
Find the column on which we will apply aggregation(sum,ave,max,min etc)
Identify the column which values will be the column header.
Specify the column values mentioned in step3 with comma separated and surrounded by square brackets.
So, if we now follow above four steps and extract information from the above sales table, it will be as below:
Year, Month, SalesAmount
SalesAmount
Month
[Jan],[Feb] ,[Mar] .... etc
We are nearly there if all the above steps made sense to you so far.
Now we have all the information we need. All we have to do now is to fill the below template with required information.
Template:
Our SQL query should look like below:
SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( [Jan],[Feb] ,[Mar],
[Apr],[May],[Jun] ,[Jul],
[Aug],[Sep] ,[Oct],[Nov] ,[Dec])
) AS PivotTable;
In the above query we have hard coded the column names. Well it's not fun when you have to specify a number of columns.
However, there is a work arround as follows:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + SalesMonth + ']'
FROM Sales
GROUP BY SalesMonth
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( ' + #columnList +' )
) AS PivotTable;'
EXEC sp_executesql #pivotsql
Hopefully this tutorial will be a help to someone somewhere.
Enjoy coding.

Custom aggregate function (concat) in SQL Server

Question: I want to write a custom aggregate function that concatenates string on group by.
So that I can do a
SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2
FROM TABLE_XY
GROUP BY FIELD1, FIELD2
All I find is SQL CRL aggregate functions, but I need SQL, without CLR.
Edit:1
The query should look like this:
SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2
FROM TABLE_XY
GROUP BY FIELD0
Edit 2:
It is true that it isn't possible without CLR.
However, the subselect answer by astander can be modified so it doesn't XML-encode special characters.
The subtle change for this is to add this after "FOR XML PATH":
,
TYPE
).value('.[1]', 'nvarchar(MAX)')
Here a few examples
DECLARE #tT table([A] varchar(200), [B] varchar(200));
INSERT INTO #tT VALUES ('T_A', 'C_A');
INSERT INTO #tT VALUES ('T_A', 'C_B');
INSERT INTO #tT VALUES ('T_B', 'C_A');
INSERT INTO #tT VALUES ('T_C', 'C_A');
INSERT INTO #tT VALUES ('T_C', 'C_B');
INSERT INTO #tT VALUES ('T_C', 'C_C');
SELECT
A AS [A]
,
(
STUFF
(
(
SELECT DISTINCT
', ' + tempT.B AS wtf
FROM #tT AS tempT
WHERE (1=1)
--AND tempT.TT_Status = 1
AND tempT.A = myT.A
ORDER BY wtf
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
, 1, 2, ''
)
) AS [B]
FROM #tT AS myT
GROUP BY A
SELECT
(
SELECT
',äöü<>' + RM_NR AS [text()]
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH('')
) AS XmlEncodedNoNothing
,
SUBSTRING
(
(
SELECT
',äöü<>' + RM_NR AS [data()]
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH('')
)
,2
,10000
) AS XmlEncodedSubstring
,
(
STUFF
(
(
SELECT ',äöü<>' + RM_NR + CHAR(10)
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
, 1, 1, ''
)
) AS XmlDecodedStuffInsteadSubstring
You cannot write custom aggregates outside of the CLR.
The only type of functions you can write in pure T-SQL are scalar and table valued functions.
Compare the pages for CREATE AGGREGATE, which only lists CLR style options, with CREATE FUNCTION, which shows T-SQL and CLR options.
Have a look at something like. This is not an aggregate function. If you wish to implement your own aggregate function, it will have to be CLR...
DECLARE #Table TABLE(
ID INT,
Val VARCHAR(50)
)
INSERT INTO #Table (ID,Val) SELECT 1, 'A'
INSERT INTO #Table (ID,Val) SELECT 1, 'B'
INSERT INTO #Table (ID,Val) SELECT 1, 'C'
INSERT INTO #Table (ID,Val) SELECT 2, 'B'
INSERT INTO #Table (ID,Val) SELECT 2, 'C'
--Concat
SELECT t.ID,
SUM(t.ID),
stuff(
(
select ',' + t1.Val
from #Table t1
where t1.ID = t.ID
order by t1.Val
for xml path('')
),1,1,'') Concats
FROM #Table t
GROUP BY t.ID
Starting from 2017 there is built-in concatenate aggregate function STRING_AGG :)
https://learn.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017
Found this link around concatenation which covers methods like
Concatenating values when the number of items are not known
Recursive CTE method
The blackbox XML methods
Using Common Language Runtime
Scalar UDF with recursion
Table valued UDF with a WHILE loop
Dynamic SQL
The Cursor approach
Non-reliable approaches
Scalar UDF with t-SQL update extension
Scalar UDF with variable concatenation in SELECT
Though it doesn't cover aggerate functions there may be some use around concatenation in there to help you with your problem.
This solution works with no need of deploy from Visual studio or dll file in server.
Copy-Paste and it Work!
https://github.com/orlando-colamatteo/ms-sql-server-group-concat-sqlclr
dbo.GROUP_CONCAT(VALUE )
dbo.GROUP_CONCAT_D(VALUE ), DELIMITER )
dbo.GROUP_CONCAT_DS(VALUE , DELIMITER , SORT_ORDER )
dbo.GROUP_CONCAT_S(VALUE , SORT_ORDER )
You could do something like what I have done below to create a custom aggregate concatenation function in pure T-SQL. Obviously I have gone with a hard coded table name and group by column but it should illustrate the approach. There is probably some way to make this a truly generic function using dynamic TSQL constructed from input parameters.
/*
User defined function to help perform concatenations as an aggregate function
Based on AdventureWorks2008R2 SalesOrderDetail table
*/
--select * from sales.SalesOrderDetail
IF EXISTS (SELECT *
FROM sysobjects
WHERE name = N'fnConcatenate')
DROP FUNCTION fnConcatenate
GO
CREATE FUNCTION fnConcatenate
(
#GroupByValue int
)
returnS varchar(8000)
as
BEGIN
DECLARE #SqlString varchar(8000)
Declare #TempStore varchar(25)
select #SqlString =''
Declare #MyCursor as Cursor
SET #MyCursor = CURSOR FAST_FORWARD
FOR
Select ProductID
From sales.SalesOrderDetail where SalesOrderID = #GroupByValue
order by SalesOrderDetailID asc
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #TempStore
WHILE ##FETCH_STATUS = 0
BEGIN
select #SqlString = ltrim(rtrim(#TempStore )) +',' + ltrim(rtrim(#SqlString))
FETCH NEXT FROM #MyCursor INTO #TempStore
END
CLOSE #MyCursor
DEALLOCATE #MyCursor
RETURN #SqlString
END
GO
select SalesOrderID, Sum(OrderQty), COUNT(*) as DetailCount , dbo.fnConcatenate(salesOrderID) as ConCatenatedProductList
from sales.SalesOrderDetail
where salesOrderID= 56805
group by SalesOrderID