SQL Pivot on Foreign Key but display different column header - sql

I had written a stored procedure that would first get a list of columns which were distinct account descriptions and use those to pivot on for getting daily monetary values per account. The problem I encountered is the end user has instances where they have the same Account description for multiple accounts so I am now using AccountID's to pivot on.
Below you will find my query to get Distinct Account ID's and then to create the pivot table. My end goal is that the current column headers for the user are useless i.e. "1504" "1505" "1683" etc. These have no meaning to them. How can I either rename column headers of a result set or somehow use a pivot based on distinct account ID's while displaying a description instead. GLAccounts.Description is the column with the description I would like to display instead of the GLAccounts.AccountID.
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(AccountID)
FROM (
SELECT DISTINCT GLAccounts.AccountID AS AccountID
FROM GeneralLedger
INNER JOIN DailyFiles ON GeneralLedger.DailyFileID = DailyFiles.DailyFileID
Inner Join GLAccounts ON GeneralLedger.AccountID = GLAccounts.AccountID
-- Get All Daily Files for company and date range
INNER JOIN
(SELECT DailyFileID
FROM DailyFiles
WHERE (DailyFiles.DailyFileDate >= CONVERT(DATETIME, #MTDStart, 102)) AND (DailyFiles.DailyFileDate <= CONVERT(DATETIME, #MTDEnd, 102))
AND CompanyID = #CompanyID) AS DAILYFILEIDS ON GeneralLedger.DailyFileID = DAILYFILEIDS.DailyFileID
) x
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = N'SELECT A.*
from(
SELECT SummedAccounts.Date, SUM(SummedAccounts.DayTotal) AS DayTotal, SummedAccounts.AccountID AS AccountID
FROM(SELECT DailyFiles.DailyFileDate AS Date, IIF(GLAccounts.FriendlyName IS NOT NULL AND GLAccounts.FriendlyName <> '''', GLAccounts.FriendlyName, GLAccounts.Description) as AccountDescription, IIF(GeneralLedger.SavedToCredit = 1,
--Saved To Credit Is True
IIF(AccountTYpes.PositiveToCredit = 1, (SUM(GeneralLedger.Credit + GeneralLedger.Debit)), (SUM(GeneralLedger.Credit + GeneralLedger.Debit) * -1)),
--Saved To Credit Is False
IIF(AccountTYpes.PositiveToCredit = 1, ( SUM(GeneralLedger.Credit + GeneralLedger.Debit) * -1), (SUM(GeneralLedger.Credit + GeneralLedger.Debit)))) AS DayTotal, GeneralLedger.SavedToCredit, AccountTypes.PositiveToCredit, GeneralLedger.AccountID AS AccountID
FROM GeneralLedger
INNER JOIN DailyFiles ON GeneralLedger.DailyFileID = DailyFiles.DailyFileID
Inner Join GLAccounts ON GeneralLedger.AccountID = GLAccounts.AccountID
INNER JOIN AccountTypes ON AccountTypes.AccountTypeID = GLAccounts.AccountTypeID
-- Get All Daily Files for company and date range
INNER JOIN
(SELECT DailyFileID
FROM DailyFiles
WHERE (DailyFiles.DailyFileDate >= #MTDStart) AND (DailyFiles.DailyFileDate <= #MTDEnd)
AND CompanyID = #CompanyID) AS DAILYFILEIDS ON GeneralLedger.DailyFileID = DAILYFILEIDS.DailyFileID
GROUP BY GeneralLedger.AccountID, DailyFiles.DailyFileDate, GLAccounts.FriendlyName, GLAccounts.Description, GeneralLedger.SavedToCredit, AccountTypes.PositiveToCredit) SummedAccounts
GROUP BY SummedAccounts.Date, SummedAccounts.AccountID
) y
pivot
(
min(DayTotal)
for AccountID in (' + #cols + ')
) A'
Result Sets Look Like:

Related

How do I use a CASE statement in a JOIN in SQL Server?

I have a stored procedure that I want to select a column if the applicationTypeId is in a list of IDs. I am trying to use the CASE statement but I don't know what I am doing wrong. I created a table and added the ApplicationTypeIds to it. The table name is #RelationshipsTypeApplicationId
In other words, what I am trying to do is the following:
If the applicationTypeId is in the #RelationshipsTypeApplicationId table
Join and select the Name column from the IGCRelationshipTypes table
Here is my original stored procedure
WITH AllCustomers AS
(
SELECT
app.ApplicationId,
cust.Name,
ApplicationTypes.TypeName AS ApplicationType,
ApplicationSources.ApplicationSourceName AS ApplicationSource,
CreatedDate = app.CreatedDate,
LastUpdateDate = app.StatusChangeDate,
app.StatusId,
InProgress = STUFF((SELECT ';'+#aw.ApplicationWorkflowName
FROM #aw
WHERE #aw.ApplicationId = app.ApplicationId
ORDER BY StepNumber
FOR XML PATH('')), 1, 1, ''),
ActionRequired = (SELECT top 1 1 FROM #aw
WHERE #aw.ApplicationId = app.ApplicationId
AND #aw.WorkflowStatusId = 6),
AccountId = STUFF((SELECT ';' + acct.AccountId
FROM #accounts AS acct
WHERE acct.ApplicationId = app.ApplicationId
ORDER BY acct.ParentAccountId
FOR XML PATH('')), 1, 1, '')
FROM
[dbo].[Applications] app
INNER JOIN
[dbo].[Customers] AS cust ON cust.CustomerId = app.CustomerId
INNER JOIN
[dbo].[ApplicationTypes] ON ApplicationTypes.ApplicationTypeId = app.ApplicationTypeId
INNER JOIN
[dbo].[ApplicationSources] ON ApplicationSources.ApplicationSourceId = app.ApplicationSourceId
WHERE
app.StatusId <> '4020-8901-64FFE33F06B1'
AND (#applicationTypeId IS NULL OR app.ApplicationTypeId = #applicationTypeId)
And here is what I updated:
WITH AllCustomers AS
(
SELECT
app.ApplicationId,
cust.Name,
ApplicationTypes.TypeName AS ApplicationType,
ApplicationSources.ApplicationSourceName AS ApplicationSource,
CreatedDate = app.CreatedDate,
LastUpdateDate = app.StatusChangeDate,
app.StatusId,
InProgress = STUFF((SELECT ';' + #aw.ApplicationWorkflowName
FROM #aw
WHERE #aw.ApplicationId = app.ApplicationId
ORDER BY StepNumber
FOR XML PATH('')), 1, 1, ''),
ActionRequired = (SELECT top 1 1 FROM #aw
WHERE #aw.ApplicationId = app.ApplicationId
AND #aw.WorkflowStatusId = 6),
AccountId = STUFF((SELECT ';' + acct.AccountId
FROM #accounts as acct
WHERE acct.ApplicationId = app.ApplicationId
ORDER BY acct.ParentAccountId
FOR XML PATH('')), 1, 1, ''),
CASE
WHEN app.ApplicationTypeId IN (SELECT * FROM #RelationshipsTypeApplicationId)
THEN relationshipType.Name
END
FROM
[dbo].[Applications] app
INNER JOIN
[dbo].[Customers] AS cust ON cust.CustomerId = app.CustomerId
INNER JOIN
[dbo].[ApplicationTypes] ON ApplicationTypes.ApplicationTypeId = app.ApplicationTypeId
INNER JOIN
[dbo].[ApplicationSources] ON ApplicationSources.ApplicationSourceId = app.ApplicationSourceId
INNER JOIN
[dbo].[IGCRelationshipTypes] AS relationshipType
ON CASE
WHEN app.ApplicationTypeId IN (SELECT * FROM #RelationshipsTypeApplicationId)
THEN (relationshipType.Name = #relationshipType)
END
WHERE
app.StatusId <> '4020-8901-64FFE33F06B1'
AND (#applicationTypeId IS NULL OR app.ApplicationTypeId = #applicationTypeId)
Here is a screenshot of the syntax error:
Can someone shed some light on what I am missing here?
CASE in T-SQL is an expression that returns a single scalar value, it is not for control of flow. See Dirty secrets of the CASE expression. You'll need something like this, but I don't have the energy to try to determine why you have a table named #RelationshipsTypeApplicationId with a single column (because you really shouldn't say IN (SELECT *)) or if you want to do something different when the match does not exist.
...
ON relationshipType.Name = CASE
WHEN app.ApplicationTypeId IN
(SELECT * FROM #RelationshipsTypeApplicationId)
THEN #relationshipType END
...

Attempting to set a non-NULL- able column's value to NULL

I am getting attempting to set a non-NULL- able column's value to NULL
error when I use FOR XML PATH('')
Code:
select
fdp.POLICYNUMBER,
fdp.INSUREDNAME,
fdp.OWNERNAME,
fdp.AGENCYCODE,
p.WFWORKSTEPNAME,
fdpc.NIGO,
fdpc.NIGOREASON,
d.NIGOREQUIREMENT,
wci.WCSTATUS,
wci.DATECOMPLETED,
fdCI.NEWUNDERWRITINGUSER,
fdr.NBAMOUNT
into
#t
from
PINewBusiness p
join
FDPolicyDetails fdp on SUBSTRING(p.CFREPKEY, 9, LEN(p.CFREPKEY)) = fdp.PARENT_CASEID
and (p.WFWORKSTEPNAME = 'PendingRequirements' or
p.WFWORKSTEPNAME = 'FollowUpRequirements' )
join
FDProcessing fdpc on SUBSTRING(p.CFREPKEY, 9, LEN(p.CFREPKEY)) = fdpc.PARENT_CASEID
join
FDRounting fdr on SUBSTRING(p.CFREPKEY, 9, LEN(p.CFREPKEY)) = fdr.PARENT_CASEID
cross apply
(select NIGOREQUIREMENT + ', '
from FDNIGORequirements nr
where nr.PARENT_CASEID = fdpc.PARENT_CASEID
for xml path('')) D (NIGOREQUIREMENT)
--join FDNIGORequirements nr on SUBSTRING(p.CFREPKEY, 9, LEN(p.CFREPKEY)) = nr.PARENT_CASEID
cross apply
(select NIGOREQUIREMENT + ', '
from FDNIGORequirements nr
where nr.PARENT_CASEID = SUBSTRING(p.CFREPKEY,9,LEN(p.CFREPKEY))
for xml path('')) D (NIGOREQUIREMENT)
join
(select max(CREATEDDATETIME) as CREATEDDATETIME, PARENT_CASEID
from FDWelcomeCall
group by PARENT_CASEID ) wc on fdr.PARENT_CASEID = wc.PARENT_CASEID
join
FDWelcomeCall wci on wci.PARENT_CASEID = wc.PARENT_CASEID
and wci.CREATEDDATETIME = wc.CREATEDDATETIME
join
FDCaseInformation fdCI on SUBSTRING(p.CFREPKEY, 9, LEN(p.CFREPKEY)) = fdCI.PARENT_CASEID
where
p.WFSTEPENTRYTIME >= #sdate
and p.WFSTEPENTRYTIME <= #edate
order by
p.WFFLOWENTRYTIME desc
If you want to keep the rows with NULL values you must replace the nulls in the column that gives you the error, to do so you can use:
SELECT COALESCE([MyColumn],__ReplacementValue__) AS [MyColumn] FROM MyTable
or
SELECT ISNULL([MyColumn],__ReplacementValue__) AS [MyColumn] FROM MyTable
You can use one of them in the inner or outer SELECT.
Otherwise, if the row should not exist if that value is null you must filter it out by adding a WHERE clause:
WHERE [MyColumn] IS NOT NULL

SQL get to select multiple names from select subquery

I am writing select statement in SQL Server. I have to add a select query.
select
acct.AccountID,
acct.Username,
acct.LastNm + ', ' + acct.FirstNm as Name ,
acct.Lastlogin,
acct.email as Email,
(select acct2.FirstNm + ' ' + acct2.LastNm as Name from tblUserAccount acct2
join tblReviewers ra2 on acct2.AccountID = ra2.ReviwerID) as Reviewers
from tblUserAccount acct
I need to get more names from a table called tblReviwers. So 1 user from the tblUserAccount table could be associated with multiple reviews.
The tblReviewers only has 3 column AnalystID, ReviewerID, and Date. When I select on the JOIN between TblUserAccount and TblReviewers on a test AccountID = AnalystID, I can get multiple ReviewerIDs, which their firstname and lastname are located in the tblUserAccount. This is the reason why I use a select subquery
When I run the query, I get the following error
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
I am trying to write a VIEW to get a data.
Any help is greatly appreciated.
A query for a column name can only contain a single record result set. If you have an entry with multiple results causes that error.
I think you are missing disclosure of a possible extra component needed in the reviewers table, the who entered it. This will result in a query similar to the following.
The aliases and relations will appear obvious, but need to be confirmed for your actual structure.
select
EnterBy.AccountID,
EnterBy.Username,
EnterBy.LastNm + ', ' + EnterBy.FirstNm as Name ,
EnterBy.Lastlogin,
EnterBy.email as Email,
ReviewBy.FirstNm + ' ' + ReviewBy.LastNm as ReviewerName
from
tblReviewers r
tblUserAccount EnterBy
on r.AccountID = EnterBy.AccountID
tblUserAccount ReviewBy
on r.ReviwerID = ReviewBy.AccountID
REVISION BASED ON ADDITIONAL DATA
Based on providing the analystID on who entered, you should be good with
select
EnterBy.AccountID,
EnterBy.Username,
EnterBy.LastNm + ', ' + EnterBy.FirstNm as Name ,
EnterBy.Lastlogin,
EnterBy.email as Email,
STUFF(( Select
', [' + ReviewBy.LastNm + ', ' + ReviewBy.FirstNm + '] ' AS [text()]
From
tblReviewers r
JOIN tblUserAccount ReviewBy
on r.ReviwerID = ReviewBy.AccountID
where
r.AnaylstID = EnterBy.AccountID
For XML PATH('')), 1, 2, '' ) as AllReviewers
from
tblUserAccount EnterBy
You cannot have a column that contains multiple rows in a single result set, it just doesn't make sense from SQL perspective. Hence the error.
You should JOIN the tblReviewers table instead of subselecting it. That will yield all Reviewers. Then, JOIN again on the tblUserAccount table to get the name of the Reviewer, and you are all set.
SELECT
acct.AccountID,
acct.Username,
acct.LastNm + ', ' + acct.FirstNm as Name ,
acct.Lastlogin,
acct.email as Email,
acct2.LastNm + ', ' + acct2.FirstNm as ReviewerName ,
FROM tblUserAccount acct
JOIN tblReviewers revi
ON on acct.AccountID = revi.ReviewerID
JOIN tblUserAccount acct2
ON on acct2.AccountID = revi.AnalystID
Starting from Sql Server 2017, You can use something like STRING_AGG function, which combines values from multiple rows into a single string.
Then, your Reviewers column in your query might look like this:
(select STRING_AGG(Name, ',') from
(select acct2.FirstNm + ' ' + acct2.LastNm as Name from tblUserAccount acct2
join tblReviewers ra2 on acct2.AccountID = ra2.ReviwerID
where ra2.AnalystID = acct.AccountID)
) as Reviewers
In this case, names(first name + last name) of the reviewers for the current user will be separated by commas.
Subselects in select lists can only be used to return one value.
In your case, use joins
SELECT
a.AccountID,
a.Username,
a.LastNm + ', ' + a.FirstNm as Name ,
a.Lastlogin,
a.email as Email,
b.LastNm + ', ' + b.FirstNm as ReviewerName
FROM
tblUserAccount a
LEFT JOIN tblReviewers r ON a.AccountID = r.AnalystID
INNER JOIN tblUserAccount b ON r.ReviewerID = b.AccountID
I also used a LEFT JOIN to the reviewers table, in case there is no reviewer defined for an analyst.
You will get several rows per analyst, if more than one reviewer is assigned to it. Usually I solve this problem in the front-end by making a report that puts the main data in a group header (the analyst) and the related date (the reviewers) in the detail section of the report.
Starting with SQL Server 2017, you can also use the STRING_AGG function to concatenate the values of string expressions.
SELECT
a.AccountID,
a.Username,
a.LastNm + ', ' + a.FirstNm AS Name ,
a.Lastlogin,
a.email AS Email,
STRING_AGG(
( SELECT b.LastNm + ' ' + b.FirstNm
FROM
tblReviewers
INNER JOIN tblUserAccount b ON r.ReviewerID = b.AccountID
WHERE
r.AnalystID = a.AccountID ),
', ') AS ReviewerName
FROM
tblUserAccount a
This allows you to return more than one reviewer in one field.

Data show by list when group by is not include data

I have a sql command like this
select mainPOID.EstAPDate,mainPOID.POID,TTm.ID,TTMAmount=TTMD.InvoiceAmount
From TTBeforeMms TTM
inner join TTBeforeMms_Detail TTMD on TTM.ID = TTMD.ID
inner join (
select distinct main.EstAPDate,main_D.POID
From AP main
left join AP_Detail main_D on main.ID = main_D.ID
where main.Type ='PA' and main.EstAPDate between '2018/6/01' AND '2018/6/15' and left(main_D.InvoiceNo,4) != '1111' ) mainPOID on TTMD.poid =mainPOID.POID and TTM.EstAPdate<=mainPOID.EstAPdate and mainPOID.POID='CM3PU18030009'
order by mainPOID.EstAPDate,mainPOID.POID
sql result will like this
My question is
How can Data show by list when group by is not include data?
For example
ID will show by list When I group by EstAPDate、POID and sum(TTMAmount)
You can store in one temp table or use CTE
CREATE TABLE [dbo].#Columnss(
espapdate date ,
poid varchar(max),
id varchar(max),amount decimal(22,6))
GO
insert into #Columnss values('2018-06-15','cm3','pt20',19988.8900)
insert into #Columnss values('2018-06-15','cm3','pt21',265.8900)
SELECT
REPLACE(ESPAPDATE,'-','/') ESPAPDATE, POID,
STUFF(
(SELECT ' , ' + OD.ID
FROM #COLUMNSS OD
WHERE OD.ESPAPDATE = O.ESPAPDATE
AND OD.POID = O.POID
FOR XML PATH('')), 1, 2, ''
) PRODUCTNAMES,SUM(AMOUNT)AMOUNT
FROM #COLUMNSS O
GROUP BY ESPAPDATE, POID
output
espapdate poid ProductNames amount
2018/06/15 cm3 pt20 , pt21 20254.780000
there are lots of CSV example using FOR XML PATH. For your case here, i am wrapping your existing query in a CTE and then from there generate the CSV string for ID
; with
cte as
(
select mainPOID.EstAPDate, mainPOID.POID, TTm.ID, TTMAmount=TTMD.InvoiceAmount
From TTBeforeMms TTM
inner join TTBeforeMms_Detail TTMD on TTM.ID = TTMD.ID
inner join (
select distinct main.EstAPDate,main_D.POID
From AP main
left join AP_Detail main_D on main.ID = main_D.ID
where main.Type ='PA'
and main.EstAPDate between '2018/6/01' AND '2018/6/15'
and left(main_D.InvoiceNo,4) != '1111'
) mainPOID on TTMD.poid = mainPOID.POID
and TTM.EstAPdate <= mainPOID.EstAPdate
and mainPOID.POID = 'CM3PU18030009'
)
SELECT EstAPDate, POID,
ID = STUFF(c.ID, 1, 1, ''),
TTMAmount = SUM(TTMAmount)
FROM cte
CROSS APPLY
(
SELECT ',' + ID
FROM cte x
WHERE x.EstAPDate = cte.EstAPDate
AND x.POID = cte.POID
FOR XML PATH ('')
) c (ID)
GROUP BY EstAPDate, POID
ORDER BY EstAPDate, POID

How to add multiple column in pivot table in SQL Server in my scenario?

I can do pivot table with single column, But I want to do it with multiple column. How to achieve it.
Here is my code with single column
SELECT ID as ItemID,
isnull([1201],0) as [1201],
isnull([1001],0) as [1001],
isnull([1302],0) as [1302],
isnull([1400],0) as [1400],
isnull([2001],0) as [2001],
isnull([1500],0) as [1500],
isnull([2400],0) as [2400],
isnull([2200],0) as [2200],
isnull([1201],0) + isnull([1001],0) + isnull([1302],0) + isnull([1400],0) + isnull([2001],0) + isnull
([2400],0) + isnull([2200],0) + isnull([1500],0) as TotalQtyCost
FROM
(
SELECT
irq.ID,
irq.ItemLookupcode,
irq.StoreID,
--ItemDynamic.SnapShotCost,
(ItemDynamic.SnapShotCost * irq.TotalQtyParent) Cost
FROM
ItemRelationQty irq
LEFT JOIN ItemDynamic with(Nolock) on ItemDynamic.ItemID = irq.ID and ItemDynamic.StoreID = irq.StoreID
inner join item with(nolock) on item.id = irq.id and item.SubDescription2 = 'lux'
) L
PIVOT
(
SUM(Cost)
FOR StoreID IN ( [1001], [1201], [1302], [1400], [2001], [2400], [2200], [1500])
)
AS PVT
ORDER BY ID
As you can see in comment line ItemDynamic.SnapShotCost that field what I want to add another column.
How to achieve using pivot?