Pivot table giving wrong result - sql

Below code gives me result set as shown in first image
SELECT dbo.tbStudent.Name, dbo.tbStudent.RegNo, dbo.tbFee.PID, dbo.tbFee.Purpose, dbo.tbFee.AmountPaid, dbo.tbFee.StudentID, dbo.tbFee.Date, dbo.tbFee.FeeID,
dbo.tbFee.SemID, dbo.tbFee.CourseID, dbo.tbFee.ModeOfPayment, dbo.tbFee.CheckNo, dbo.tbFee.DDNo, dbo.tbFee.HostelDDNo, dbo.tbFee.FRID,
dbo.tbStudent.Parentage, dbo.tbCourse.Name AS Course, ISNULL(dbo.tbSemester.SemName, ' + #st +') AS Semester
FROM dbo.tbFee INNER JOIN
dbo.tbStudent ON dbo.tbFee.StudentID = dbo.tbStudent.StudentID INNER JOIN
dbo.tbCourse ON dbo.tbFee.CourseID = dbo.tbCourse.CourseID LEFT OUTER JOIN
dbo.tbSemester ON dbo.tbFee.SemID = dbo.tbSemester.SemID Where tbFee.SemID=1
However using Pivot table I need result as below:
My code for pivot table is :
SET #values = '';
If(#SemID=0)
BEGIN
SELECT #values = #values +'['+ CAST(PurPose AS varchar(max))+ ']' + ','
FROM tbFee Where CourseID=#CourseID
SET #values = SUBSTRING(#values, 1, Len(#values) - 1)
END
ELSE
BEGIN
SELECT #values = #values +'['+ CAST(PurPose AS varchar(max))+ ']' + ','
FROM tbFee Where SemID=#SemID
SET #values = SUBSTRING(#values, 1, Len(#values) - 1)
END
Declare #st nvarchar(max)
set #st='''Not Available''';
declare #q nvarchar(max)
set #q = '
Select * from(
SELECT dbo.tbStudent.Name, dbo.tbStudent.RegNo, dbo.tbFee.PID, dbo.tbFee.Purpose, dbo.tbFee.AmountPaid, dbo.tbFee.StudentID, dbo.tbFee.Date, dbo.tbFee.FeeID,
dbo.tbFee.SemID, dbo.tbFee.CourseID, dbo.tbFee.ModeOfPayment, dbo.tbFee.CheckNo, dbo.tbFee.DDNo, dbo.tbFee.HostelDDNo, dbo.tbFee.FRID,
dbo.tbStudent.Parentage, dbo.tbCourse.Name AS Course, ISNULL(dbo.tbSemester.SemName, ' + #st +') AS Semester
FROM dbo.tbFee INNER JOIN
dbo.tbStudent ON dbo.tbFee.StudentID = dbo.tbStudent.StudentID INNER JOIN
dbo.tbCourse ON dbo.tbFee.CourseID = dbo.tbCourse.CourseID LEFT OUTER JOIN
dbo.tbSemester ON dbo.tbFee.SemID = dbo.tbSemester.SemID Where tbFee.SemID=1
) as x
pivot (
max(AmountPaid)
for Purpose in (' + #values + ')
) as pvt
'
exec (#q)
I am getting Values of Purpose columns in #values due to the reason that number of rows can change. However instead of getting result as single row for same student having same regNo , I am getting below result :
But what I am getting is below:

In the source query for your PIVOT, you should only specify those columns which are involved in the actual pivot - namely dbo.tbStudent.Name, dbo.tbStudent.RegNo, dbo.tbFee.Purpose, dbo.tbFee.AmountPaid.
SELECT
dbo.tbStudent.Name,
dbo.tbStudent.RegNo,
dbo.tbFee.Purpose,
dbo.tbFee.AmountPaid
FROM
dbo.tbFee
INNER JOIN dbo.tbStudent ON dbo.tbFee.StudentID = dbo.tbStudent.StudentID
INNER JOIN dbo.tbCourse ON dbo.tbFee.CourseID = dbo.tbCourse.CourseID
LEFT OUTER JOIN dbo.tbSemester ON dbo.tbFee.SemID = dbo.tbSemester.SemID
Where tbFee.SemID=1
If any other columns apart from these are present, they will be factored into the pivot computation, and you will get multiple rows accordingly.

Related

Create a pivot of same columns in to 1 row

I'm using a SQL Server, I've a query which return the data of all the fields, The main thing is that 1 field can belongs to multiple records, the record ID differentiate them.
I've a data set like this.
This is my current data set
My current query:
Select fd.FieldName ,FV.FieldID, Data , R.RecordID from FieldValues FV
Inner Join Records R on R.RecordID = FV.RecordID
Inner Join Forms F On f.FormID = R.FormID
Inner join Fields fd on fd.FieldID = fv.FieldID
Where R.RecordID IN (45,46)
I need to create 1 row of each columns that belongs to the same RecordID like this.
Service Name Location city VendorCode RecordID
Raj ABC LOCATION ABC CITY 32 45
BEN ABC LOCATION ABC CITY -- 46
The above is my desired output.
I've tried with pivot but have not succeeded.
If you don't like to deal with dynamic pivot and you do know the key of the rows you want to convert into columns, you can use standard sql with max and case when
select
max(case fd.FieldName when 'SelectService' then Data else null end) as ServiceName,
max(case fd.FieldName when 'EnterYourLocation' then Data else null end) as Location,
max(case fd.FieldName when 'City' then Data else null end) as city,
max(case fd.FieldName when 'VendorCodeOption' then Data else null end) as VendorCode,
R.RecordId
from FieldValues FV
Inner Join Records R on R.RecordID = FV.RecordID
Inner Join Forms F On f.FormID = R.FormID
Inner join Fields fd on fd.FieldID = fv.FieldID
where R.RecordID IN (45,46)
group by R.RecordId
This is the solution with pivot but it is missing to include adjust joins
declare #columns varchar(max) set #columns = ''
select #columns = coalesce(#columns + '[' + cast(col as varchar(MAX)) + '],', '')
FROM ( select FieldName as col from FieldValues group by FieldName ) m
set #columns = left(#columns,LEN(#columns)-1)
DECLARE #SQLString nvarchar(max);
set #SQLString = '
select * from
( select RecordId, FieldName, Data from FieldValues) m
PIVOT
( MAX(Data)
FOR FieldName in (' + #columns + ')
) AS PVT'
EXECUTE sp_executesql #SQLString

How to use CTE with a T-SQL variable in SQL Server 2012?

I have a T-SQL variable containing another variable and I want to create a CTE based on this T-SQL variable.
I have tried printing the T-SQL variable in CTE but it is not working.
declare
#Hid nvarchar(5),
#mysql nvarchar(max)
set #Hid = 1
set #mysql = 'SELECT
CONVERT(DATE, GETDATE()) AS RptDate,
a.[key],
b.[Country], b.AcNumber, b.AcName,
DATEDIFF([d], a.[ItemDate], GETDATE()) AS [IAge],
[comment] AS OfficerComment,
(SELECT dbo.fnMgrCmnt(2,' + #Hid + ', a.rowid, 1)),
c.[firstname] + '' '' + c.[lastname] AS Modifiedby,
d.hubId,
'''' AS origin,
b.Authname,
CONVERT(DECIMAL(38, 2), a.amount) AS Amt,
b.AcOwner,
a.rowid AS rowid
FROM [dbo].[T_AccRecon] a
INNER JOIN [dbo].[VW_TMain] b ON a.[key] = b.[Key]
LEFT JOIN [dbo].[users] c ON a.Modifiedby = c.userId
LEFT JOIN [dbo].[Country] d ON b.Country = d.code
AND b.AcOwner = d.hubcode'
I want to create a CTE from this #mysql variable. I tried with
;WITH MY_CTE AS (#mysql)
but it is not working. What can be done for this?

SQL Server query result by column to row

This is my sample code:
SQL Fiddle
I need it to result like this:
category outlet1 outlet2 outlet3
Sale 70 20 40
Expense 250 130 200
How can I do this?
EDIT: My outlets are not fixed, sorry for not telling this beforehand.
You can solve your particular problem using conditional aggregation:
SELECT c.category,
SUM(CASE WHEN o.outletname = 'Outlet1' THEN t.amt ELSE 0 END) as Outlet1,
SUM(CASE WHEN o.outletname = 'Outlet2' THEN t.amt ELSE 0 END) as Outlet2,
SUM(CASE WHEN o.outletname = 'Outlet3' THEN t.amt ELSE 0 END) as Outlet3
FROM tblcategory c INNER JOIN
tbltran t
ON t.catid = c.id INNER JOIN
tbloutlet o
ON o.id = t.outletid
GROUP BY c.category;
If the outlet names are not fixed, then you need dynamic SQL. The problem cannot be solve using a single SELECT query.
Here with dynamic Outlets http://sqlfiddle.com/#!18/a7b09/25
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(outletname)
from tbloutlet
group by outletname
order by outletname
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT category,' + #cols + ' from
(
SELECT c.category, o.outletname, SUM(t.amt) as amt
FROM tblcategory c
INNER JOIN tbltran t ON t.catid = c.id
INNER JOIN tbloutlet o ON o.id = t.outletid
GROUP BY c.category, o.outletname
) x
pivot
(
sum(amt)
for outletname in (' + #cols + ')
) p '
execute(#query);
you may also use the PIVOT operator
SELECT *
FROM
(
SELECT category, outletname, amt
FROM tblcategory c
INNER JOIN tbltran t ON t.catid = c.id
INNER JOIN tbloutlet o ON o.id = t.outletid
) d
PIVOT
(
SUM(amt)
FOR outletname in ([Outlet1] , [Outlet2] , [Outlet3])
) p
EDIT : below is the Dynamic SQL version
declare #Outlets nvarchar(max),
#SQL nvarchar(max)
select #Outlets = isnull(#Outlets + ',', '') + quotename(outlet)
from outlet
select #SQL = '
SELECT *
FROM
(
SELECT category, outletname, amt
FROM tblcategory c
INNER JOIN tbltran t ON t.catid = c.id
INNER JOIN tbloutlet o ON o.id = t.outletid
) d
PIVOT
(
SUM(amt)
FOR outletname in (' + #Outlets + ')
) p'
print #SQL -- print out for verification
exec sp_executesql #SQL

SQL Server Flatten Data

I have 3 tables:
tUsers
-uid
tColors
-colorid
-colorname
tColors_User_Detail
-uid_fk
-colorid_fk
Users select which colors they like, and only the colors they like. This creates records in tColors_User_Detail. I need to flatten this out, so that each user has one record with the color from tColors as a column name, and they have a True/False value in the row for each color depending on if they had a record in tColors_User_Detail. If the user did not have a color selected in tColors_User_Detail, it would be a False value in the specific color column. And, if they do have a record in tColors_User_Detail for a color, it would be a true value for the corresponding color column.
Any help appreciated.
Here's a basic PIVOT example with a COALESCE to show 'false' if no value is available. This assumes you have to hard-code the names of the colors for the column names.
DECLARE #tUsers TABLE ([uid] INT)
DECLARE #tColors TABLE ([colorid] INT, [colorname] VARCHAR(50))
DECLARE #tColors_User_Detail TABLE ([uid_fk] INT, [colorid_fk] INT)
INSERT #tUsers VALUES (1),(2)
INSERT #tColors VALUES (1,'Blue'),(2,'Red'),(3,'Green')
INSERT #tColors_User_Detail VALUES (1,1),(1,2),(1,3),(2,1)
SELECT
uid,
COALESCE([Red], 'False') AS [Red],
COALESCE([Blue], 'False') AS [Blue],
COALESCE([Green], 'False') AS [Green]
FROM #tUsers U
LEFT OUTER JOIN #tColors_User_Detail CUD
ON CUD.uid_fk = U.uid
LEFT OUTER JOIN #tColors C
ON C.colorid = CUD.colorid_fk
PIVOT (MAX(colorname) FOR colorname IN (
[Red],
[Blue],
[Green]
)) PVT
If you want to let the columns be dynamic from the colors, you'll have to use dynamic sql.
DECLARE #Sql VARCHAR(1000) =
'SELECT uid'
+ (SELECT ', CASE WHEN [' + [colorname] + '] IS NOT NULL THEN ''True'' ELSE ''False'' END AS [' + [colorname] + ']' AS [text()] FROM tColors FOR XML PATH(''))
+ ' FROM tUsers U
LEFT OUTER JOIN tColors_User_Detail CUD
ON CUD.uid_fk = U.uid
LEFT OUTER JOIN tColors C
ON C.colorid = CUD.colorid_fk
PIVOT (MAX(colorname) FOR colorname IN ('
+ SUBSTRING((SELECT ',[' + [colorname] + ']' AS [text()] FROM tColors FOR XML PATH('')), 2, 1000)
+ ')) PVT'
EXEC (#Sql)
What flavor of SQL?
Something along the lines of:
http://sqlfiddle.com/#!6/ec4e2
SELECT U.uid
, C.colorid
, C.colorname
, ( CASE WHEN cud.uid_fk IS NOT NULL THEN 'True' ELSE 'False' END ) AS ColorChosen
FROM tUsers U
FULL OUTER JOIN tColors C ON 1=1
LEFT OUTER JOIN tColors_User_Detail cud ON
U.uid = cud.uid_fk
AND C.colorid = cud.colorID_FK
EDIT: I missed the pivot for one row per user. Meeting time though. Be back in a bit.

For Nvarchar(Max) I am only getting 4000 characters in TSQL?

This is for SS 2005.
Why I am i only getting 4000 characters and not 8000?
It truncates the string #SQL1 at 4000.
ALTER PROCEDURE sp_AlloctionReport(
#where NVARCHAR(1000),
#alldate NVARCHAR(200),
#alldateprevweek NVARCHAR(200))
AS
DECLARE #SQL1 NVARCHAR(Max)
SET #SQL1 = 'SELECT DISTINCT VenueInfo.VenueID, VenueInfo.VenueName, VenuePanels.PanelID,
VenueInfo.CompanyName, VenuePanels.ProductCode, VenuePanels.MF, VenueInfo.Address1,
VenueInfo.Address2, '' As AllocationDate, '' As AbbreviationCode, VenueInfo.Suburb, VenueInfo.Route, VenueInfo.ContactFirstName,
VenueInfo.ContactLastName, VenueInfo.SuitableTime, VenueInfo.OldVenueName,
VenueCategories.Category, VenueInfo.Phone, VenuePanels.Location, VenuePanels.Comment,
[VenueCategories].[Category] + '' Allocations'' AS ReportHeader,
ljs.AbbreviationCode AS PrevWeekCampaign
FROM (((VenueInfo INNER JOIN VenuePanels ON VenueInfo.VenueID = VenuePanels.VenueID)
INNER JOIN VenueCategories ON VenueInfo.CategoryID = VenueCategories.CategoryID)
LEFT JOIN (SELECT CampaignProductions.AbbreviationCode, VenuePanels.PanelID, CampaignAllocations.AllocationDate
FROM (((VenueInfo INNER JOIN VenuePanels ON VenueInfo.VenueID=VenuePanels.VenueID) INNER JOIN CampaignAllocations ON VenuePanels.PanelID=CampaignAllocations.PanelID) INNER JOIN CampaignProductions ON CampaignAllocations.CampaignID=CampaignProductions.CampaignID) INNER JOIN VenueCategories ON VenueInfo.CategoryID=VenueCategories.CategoryID
WHERE ' + #alldateprevweek + ') ljs
ON VenuePanels.PanelID = ljs.PanelID)
INNER JOIN (SELECT VenueInfo.VenueID, VenuePanels.PanelID, VenueInfo.VenueName, VenueInfo.CompanyName, VenuePanels.ProductCode,
VenuePanels.MF, VenueInfo.Address1, VenueInfo.Address2, CampaignAllocations.AllocationDate,
CampaignProductions.AbbreviationCode, VenueInfo.Suburb, VenueInfo.Route, VenueInfo.ContactFirstName,
VenueInfo.ContactLastName, VenueInfo.SuitableTime, VenueInfo.OldVenueName, VenueCategories.Category,
VenueInfo.Phone, VenuePanels.Location, VenuePanels.Comment, [Category] + '' Allocations'' AS ReportHeader,
ljs2.AbbreviationCode AS PrevWeekCampaign
FROM ((((VenueInfo INNER JOIN VenuePanels ON VenueInfo.VenueID = VenuePanels.VenueID)
INNER JOIN CampaignAllocations ON VenuePanels.PanelID = CampaignAllocations.PanelID)
INNER JOIN CampaignProductions ON CampaignAllocations.CampaignID = CampaignProductions.CampaignID)
INNER JOIN VenueCategories ON VenueInfo.CategoryID = VenueCategories.CategoryID)
LEFT JOIN (SELECT CampaignProductions.AbbreviationCode, VenuePanels.PanelID, CampaignAllocations.AllocationDate
FROM (((VenueInfo INNER JOIN VenuePanels ON VenueInfo.VenueID=VenuePanels.VenueID) INNER JOIN CampaignAllocations ON VenuePanels.PanelID=CampaignAllocations.PanelID) INNER JOIN CampaignProductions ON CampaignAllocations.CampaignID=CampaignProductions.CampaignID) INNER JOIN VenueCategories ON VenueInfo.CategoryID=VenueCategories.CategoryID
WHERE ' + #alldateprevweek + ') ljs2
ON VenuePanels.PanelID = ljs2.PanelID
WHERE ' + #alldate + ' AND ' + #where + ') ljs3
ON VenueInfo.VenueID = ljs3.VenueID
WHERE (((VenuePanels.PanelID)<>ljs3.[PanelID] And
(VenuePanels.PanelID) Not In (SELECT PanelID FROM CampaignAllocations WHERE ' + #alldateprevweek + '))
AND ' + #where + ')
UNION ALL
SELECT VenueInfo.VenueID, VenueInfo.VenueName, VenuePanels.PanelID, VenueInfo.CompanyName, VenuePanels.ProductCode,
VenuePanels.MF, VenueInfo.Address1, VenueInfo.Address2, CampaignAllocations.AllocationDate,
CampaignProductions.AbbreviationCode, VenueInfo.Suburb, VenueInfo.Route, VenueInfo.ContactFirstName,
VenueInfo.ContactLastName, VenueInfo.SuitableTime, VenueInfo.OldVenueName, VenueCategories.Category,
VenueInfo.Phone, VenuePanels.Location, VenuePanels.Comment, [Category] + '' Allocations'' AS ReportHeader,
ljs.AbbreviationCode AS PrevWeekCampaign
FROM ((((VenueInfo INNER JOIN VenuePanels ON VenueInfo.VenueID = VenuePanels.VenueID)
INNER JOIN CampaignAllocations ON VenuePanels.PanelID = CampaignAllocations.PanelID)
INNER JOIN CampaignProductions ON CampaignAllocations.CampaignID = CampaignProductions.CampaignID)
INNER JOIN VenueCategories ON VenueInfo.CategoryID = VenueCategories.CategoryID)
LEFT JOIN (SELECT CampaignProductions.AbbreviationCode, VenuePanels.PanelID, CampaignAllocations.AllocationDate
FROM (((VenueInfo INNER JOIN VenuePanels ON VenueInfo.VenueID=VenuePanels.VenueID) INNER JOIN CampaignAllocations ON VenuePanels.PanelID=CampaignAllocations.PanelID) INNER JOIN CampaignProductions ON CampaignAllocations.CampaignID=CampaignProductions.CampaignID) INNER JOIN VenueCategories ON VenueInfo.CategoryID=VenueCategories.CategoryID
WHERE ' + #alldateprevweek + ') ljs
ON VenuePanels.PanelID = ljs.PanelID
WHERE ' + #alldate + ' AND ' + #where
Select #SQL1
You have declared this as nvarchar(max) which allows 2GB of data so it will store 2GB.
What is happening:
The datatype is not yet nvarchar(max) until assignment to #sql1
Before that, it's a collection of strings, each less than 4000 (constants)
You are concatenating short constants with short variables (short = < 4000)
So you have 4000 characters put into #sql1
So, you have make sure you have nvarchar(max) on the right hand side.
One idea. The 2nd line concatenates nvarchar(max) with a constant = nvarchar(max)
SET #SQL1 = ''
SET #SQL1 = #SQL1 + 'SELECT DISTINCT Venue...
....
It's no different to the integer division that happens in every langauge.
declare #myvar float
set #myvar = 1/2 --gives zero because it's integer on the right
Operator precedence (infers datatype precedence) is always "assignment" last... why should unicode strings in SQL Server be any different?
Update: gbn's answer is right, and I was wrong. As MSDN points out, nvarchar(max) supports up to 2^31-1 bytes of data, stored as UCS-2 (2 bytes per character, plus 2 for BOM). Your problem seems to be with string concatenation, not data type limits.
That said, if you're using it to build a SQL string, why not use VARCHAR? Do you have field names that aren't representable by the database's native character set (usually Latin-1)?
Finally -- you could simplify your entire problem by just not using dynamic SQL in your stored procedure. Create some table-valued functions that take your where-clause strings and return tables, and then just JOIN them in your procedure. As a bonus it will almost certainly be much faster, since at very least the database will be able to cache the SP body as a prepared statement.
i resolve problem
just include N character before every string and problem solved
for example
declare #sql nvarchar(max) = '' + #Where + 'SomeThing';
must be
declare #sql nvarchar(max) = N'' + #Where + N'SomeThing';
if you set string to empty also must set N''
if #where is null
set #where = N''
:-) simple answer