TSQL - Unpivot multiple columns - sql-server

How can I unpivot multiple columns in "one"?
Right now I have an unpivot for each column but this creates a lot of empty rows.
See the screenshot please.
At the top you see the input data. At the moment I'm at the table in the middle with this code:
SELECT [ID], [RowNumber], [Year], [Sales] FROM (
SELECT ID, RowNumber, [Sales 2013] as [2013], [Sales 2014] as [2014]
FROM mytable) p UNPIVOT (
[Sales] FOR [Year] IN ([2013], [2014]) )AS unpvt ;
But I think it would be much better to get to the bottom table structure since the actual data contains more columns and more years to deal with.
Here's a Fiddle with the sample data.
Hope you can show me a way to get there.
Thank you.

SELECT [ID],
[RowNumber],
[Year],
Sales,
Budget
FROM mytable
CROSS APPLY (VALUES (2013, [Sales 2013], [Budget 2013]),
(2014, [Sales 2014], [Budget 2014]) )
V([Year], Sales, Budget)
SQL Fiddle

One approach is to repivot after unpivoting - like so:
select [Id], [Year], [Sales], [Budget], [Actual] from
(SELECT [Id],
Left([Colhead], charindex(' ',[Colhead])-1) [Category],
Right([Colhead], len([Colhead])-charindex(' ',[Colhead])) [Year],
[Figures]
FROM (SELECT * FROM mytable) p
UNPIVOT ([Figures] FOR [Colhead] IN
([Sales 2013],[Sales 2014],[Budget 2013],[Budget 2014],[Actual 2013],[Actual 2014])
)
AS unpvt) as u
pivot
(max([Figures]) for [Category] in ([Sales], [Budget], [Actual])) as p
SQLFiddle here.

Related

How to GROUP BY records with specific Columns only

I have this query containing GROUP BY clause.
I only want to apply GROUP BY on FolioNumber only because the data in other columns is changing rapidly for same record due to UPDATE.
Here is the sample Query, I hope you don't need sample data and understand the query structure as this pretty simple.
SELECT MAX(TransactionNo) [TransactionNo], [FolioNo], [SalesOrderDate], [Center], [Company], [Customer],
[DeliveryAddress], [OfficialPhoneNo], [Item], [OrderQty], [UnitPrice], [Description], [FinishType],
[Remarks], [OrderNo], [BookNo], [CustomerAddress], [Customer], [DeliveryDate], [SalePerson]
[Status], [Discount], [NetAmount], [GrossAmount], [InvoiceDiscount], [GSTTax], [DateFrom], [DateTo]
FROM tblPageIndex
GROUP BY [FolioNo]
You need to use Over clause. find below sample query might will help you. As you have not provide any data set for unit test.
SELECT MAX(TransactionNo) over (partition by FolioNo) as [TransactionNo], [FolioNo], [SalesOrderDate], [Center], [Company], [Customer],
[DeliveryAddress], [OfficialPhoneNo], [Item], [OrderQty], [UnitPrice], [Description], [FinishType],
[Remarks], [OrderNo], [BookNo], [CustomerAddress], [Customer], [DeliveryDate], [SalePerson]
[Status], [Discount], [NetAmount], [GrossAmount], [InvoiceDiscount], [GSTTax], [DateFrom], [DateTo]
FROM tblPageIndex
I think you want data associated with each FoliyoNo where TransactionNo is maximum.
You can use ROW_NUMBER() to group the rows based on FoliyoNo and select the row with maximum value for TransactionNo
;WITH CTE AS (
SELECT [TransactionNo], [FolioNo], [SalesOrderDate], [Center], [Company], [Customer],
[DeliveryAddress], [OfficialPhoneNo], [Item], [OrderQty], [UnitPrice],
[Description], [FinishType], [Remarks], [OrderNo], [BookNo], [CustomerAddress],
[Customer], [DeliveryDate], [SalePerson]
[Status], [Discount], [NetAmount], [GrossAmount], [InvoiceDiscount],
[GSTTax], [DateFrom], [DateTo],
ROW_NUMBER() OVER(PARTITION BY [FolioNo] ORDER BY TransactionNo DESC) AS RN
FROM tblPageIndex
)
SELECT * FROM CTE WHERE RN = 1
From the comments, it turns out to know that you want all the rows with maximum TransactionNo in each FoliyoNo.
So you can change the ROW_NUMBER() to RANK() to achieve the desired result.

How to Find Ffifth Youngest Employee by DOB in Sql Server

What will be sql query for the fifth youngest employee?
Below query is wrong? please help
SELECT EmpID, EmpName, EMPDOB,
ORDER BY EMPDOB DESC
WHERE ROWNUMBER = 5
FROM dbo.EMP
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY EMPDOB ASC) AS rownumber,
EmpID, EmpName, EMPDOB
FROM EMPLOYEE
) AS foo
WHERE rownumber = 5
The following query will solve your problem:
SELECT TOP 1
T.EmpID
, T.EmpName
, T.EMPDOB
FROM (SELECT TOP 5 * FROM dbo.EMP ORDER BY EMPDOB DESC) AS T
Note: This solution assumes that EMPDOB is in a date or integer based format.

My results of MONTH table has duplicates DISTINCT will not work for me

Query:
SELECT
MONTH(orderdate) AS [Month], COUNT(orderid) AS [Number of Orders]
FROM
Sales.Orders
WHERE
YEAR(orderdate) = 2007
GROUP BY
orderdate;
Your group by statement is wrong.
You should "GROUP BY MONTH(orderdate)"

Using Pivoting in SQL Server (Error) - Invalid column name

SELECT
[Reg. number], [Surname],
[SESREFDATETIME1], [ATTENDANCE1],
[SESREFDATETIME2], [ATTENDANCE2],
[SESREFDATETIME3], [ATTENDANCE3],
[SESREFDATETIME4], [ATTENDANCE4]
FROM
(SELECT
[Reg. number], [Surname],
col + CAST(rn AS varchar(10)) col,
value
FROM
(SELECT
[Reg. number], Surname,
row_number() over(partition by [Reg. number] order by SESREFDATETIME) rn
FROM #Temp) t
CROSS APPLY
(SELECT 'SESREFDATETIME', SESREFDATETIME
UNION ALL
SELECT 'ATTENDANCE', ATTENDANCE) c (col, value)
) x
PIVOT
(max(value)
for col in ([SESREFDATETIME1], [ATTENDANCE1], [SESREFDATETIME2],[ATTENDANCE2], [SESREFDATETIME3], [ATTENDANCE3], [SESREFDATETIME4],[ATTENDANCE4])
) p;
In my procedure I created a #temp temporary table and I tried to show multiple lines in multiple columns. The reason I used pivot because this code created dynamically and number of rows aren't known. I am running the code but it gives error. I am going to be crazy. Can't find where is the error. It shows that in cross apply there is invalid column name. I think there is another error. And it shows error in wrong side.
For testing table format is as following
Create table #Temp
(
[Reg. number] int,
[Surname] Varchar(50),
SESREFDATETIME Varchar(80),
ATTENDANCE Varchar(10)
)
Example data format is
2005162 Abasov 04/09/2014 09:00 - 10:00 Y
2005458 Baxşiyev 15/04/2015 01:00 - 04:00 NULL
2005458 Baxşiyev 16/09/2014 14:00 - 17:00 Y
2005538 Abbasbəyli 13/10/2014 12:00 - 15:00 Y
Your 'cross apply x' can't see SESREFDATETIME and ATTENDANCE, if you include those columns in your 'subselect t' the cross apply part can get the values
Try this:
SELECT
[Reg. number], [Surname],
[SESREFDATETIME1], [ATTENDANCE1],
[SESREFDATETIME2], [ATTENDANCE2],
[SESREFDATETIME3], [ATTENDANCE3],
[SESREFDATETIME4], [ATTENDANCE4]
FROM
(SELECT
[Reg. number], [Surname],
col + CAST(rn AS varchar(10)) col,
value
FROM
(SELECT
[Reg. number], Surname,
row_number() over(partition by [Reg. number] order by SESREFDATETIME) rn,
SESREFDATETIME,
ATTENDANCE
FROM #Temp) t
CROSS APPLY
(SELECT 'SESREFDATETIME', SESREFDATETIME
UNION ALL
SELECT 'ATTENDANCE', ATTENDANCE) c (col, value)
) x
PIVOT
(max(value)
for col in ([SESREFDATETIME1], [ATTENDANCE1], [SESREFDATETIME2],[ATTENDANCE2], [SESREFDATETIME3], [ATTENDANCE3], [SESREFDATETIME4],[ATTENDANCE4])
) p;

Conditional filter with Having in SQL Server

I'm using SQL Server 2016. I'm trying to figure out how to conditionally filter with a having clause.
I want to give me a list of all IDs, date, and username that if the date is before 2018 show me that users with 2-12 IDs, if the date is after 2018 then show me the users with 2-24 IDs. I can only think of unioning the codes but that increases the processing time, is there another way to do this quicker?
Original Code:
select [id], [date], [user name]
from [table]
where [date] < '2018-01-01'
group by [id], [date], [user name]
having count([id]) between 2 and 12) b
UNION Idea:
select [id], [date], [user name]
from [table]
where [date] < '2018-01-01'
group by [id], [date], [user name]
having count([id]) between 2 and 12) b
UNION
select [id], [date], [user name]
from [table]
where [date] >= '2018-01-01'
group by [id], [date], [user name]
having count([id]) between 2 and 24) b
You can use OR in your HAVING statement:
select [id], [date], [user name]
from [table]
group by [id], [date], [user name]
having (
(count([id]) between 2 and 12 AND [date] < '2018-01-01')
OR
(count([id]) between 2 and 24 AND [date] >= '2018-01-01')
);
This only works if the columns you are referencing in your HAVING clause are aggregates or included in the GROUP BY clause

Resources