SQL - Pivoting where the rows of one table are the column titles of another - sql

I'm looking to create a table (shown below) more in the form of a matrix, where the column titles are variables.
#ACTable AS ACT
#WCTable AS WCT
The temp table, derived from #ProductionTable AS PT
The output I'm looking for looks like this. Essentially I want the ACT.AC running as column titles, the WCT.WC running down, and counting how many were ActFin on Nov 6th. The colour shows the matching associations. I'll coalesce the rest after, not too concerned about NULLs or 0s.
The query so far (it fails at the FOR statement)
SELECT * FROM
(
SELECT
PT.ParentPart,
ACT.AC,
WCT.WC,
PT.ActFin
FROM #ProductionTable AS PT
INNER JOIN #WCTable AS WCT ON WCT.WC = PT.WC
INNER JOIN #ACTable AS ACT ON PT.AC = ACT.AC
) t
PIVOT(
COUNT(CASE
WHEN
PT.ActFin > '2019-11-06' --count
THEN
1
END)
FOR ACT.AC IN ( --this is where things fall apart
'54',
'53',
'52')
)
Is this possible?

The columns in the FOR clause need to be wrapped in []:
SELECT * FROM
(
SELECT
PT.ParentPart,
ACT.AC,
WCT.WC,
PT.ActFin
FROM #ProductionTable AS PT
INNER JOIN #WCTable AS WCT ON WCT.WC = PT.WC
INNER JOIN #ACTable AS ACT ON PT.AC = ACT.AC
) t
PIVOT(
COUNT(CASE
WHEN
PT.ActFin > '2019-11-06' --count
THEN
1
END)
FOR ACT.AC IN ( --this is where things fall apart
[54],
[53],
[52])
)

Related

Enter data for missing category in snowflake

I have a table like
For each keyword, there are 2 devices - mobile and desktop. If entry for only one device is found, then it should automatically create the entry for other device keeping the data in rest of the columns same. I am currently doing a full outer join which is working fine for the case where one device category is missing but generating duplicates where both devices are present. For example,
my current query is giving the result as
select a.keyword, b.device, a.rating
from kw a full outer join kw b
on a.keyword=b.keyword and a.rating=b.rating
How do I get the result as
The first step will be to identify records that don't have a paired record. There's a couple of ways to do this, but the easiest is probably just a quick GROUP BY/HAVING:
SELECT keyword
FROM kw
GROUP BY keyword
HAVING COUNT(*) = 1
You can those join those results back into the original table to generate the new records that are needed:
SELECT sk.keyword,
CASE WHEN kw.device = 'mobile' THEN 'desktop' ELSE 'mobile' END as device,
kw.rating
FROM
(
SELECT keyword
FROM kw
GROUP BY keyword
HAVING COUNT(*) = 1
)sk
INNER JOIN kw ON kw.keyword = sk.keyword
Then you can UNION back in the original table to bring your new records and existing records into a single result set:
SELECT sk.keyword,
CASE WHEN kw.device = 'mobile' THEN 'desktop' ELSE 'mobile' END as device,
kw.rating
FROM
(
SELECT keyword
FROM kw
GROUP BY keyword
HAVING COUNT(*) = 1
)sk
INNER JOIN kw ON kw.keyword = sk.keyword
UNION ALL
SELECT * FROM kw;
As another option that will scale if you add in more 'devices' is to cross join all the potential device/keyword combinations and then left join to your original table:
SELECT
fe.keyword,
fe.device,
CASE WHEN kw.rating IS NULL THEN max(rating) OVER (PARTITION BY fe.keyword) ELSE kw.rating END AS rating
FROM
(
SELECT DISTINCT kw.keyword, kw2.device
FROM kw, kw kw2
) fe
LEFT OUTER JOIN kw ON kw.keyword = fe.keyword
AND kw.device = fe.device;

SQL Inner Join and nearest row to date

I dont't get it. I changed some of the code. In the WPLEVENT Table are a lot of Events per person. In the Persab-Table are the Persons with their History. Now I need the from the Persab Table just that row wich matches the persab.gltab Date nearest to the WPLEVENT.vdat Date. So all rows from the WPLEVENT, but just the one matching row from the PERSAB-Table.
SELECT
persab.name,
persab.vorname,
vdat,
eventstart,
persab.rc1,
persab.rc2
FROM wplevent
INNER JOIN
persab ON WPLEVENT.PersID = persab.PRIMKEY
INNER JOIN
(SELECT TOP 1 persab.rc1
FROM PERSAB
WHERE persab.gltab <= getdate() --/ Should be wplevent.vdat instead of getdate()
) NewTable ON wplevent.persid = persab.primkey
WHERE
persid ='100458'
ORDER BY vdat DESC
Need to use the MAX() function with the proper syntax by supplying an expression like MAX(persab.rc1). Also need to use GROUP BY for the second column rc2 in the subquery (although it looks like you do not need it). Finally you are missing the ON clause for the final INNER JOIN. I can update the answer to fix the query if you provide that information.
SELECT
Z1PERS.NAME
, Z1PERS.VORNAME
, WPLEVENT.VDat
, WPLEVENT.EventStart
, WPLEVENT.EventStop
, WPLEVENT.PEPGROUP
, Z1SGRP.TXXT
, PERSAB.GLTAB
, Z1PERS.PRIMKEY AS Expr1
, PERSAB.PRIMKEY
FROM
Z1PERS
INNER JOIN
WPLEVENT ON Z1PERS.PRIMKEY = WPLEVENT.PersID
INNER JOIN
Z1SGRP ON WPLEVENT.PEPGROUP = Z1SGRP.GRUPPE
INNER JOIN
(
SELECT MAX(Persab.rc1) --Fixed MAX expression
, persab.rc2
FROM
persab
GROUP BY
persab.rc2 --Need to group on rc2 if you want that column in the query otherwise remove this AND the rc2 column from select list
WHERE
WPLEVENT.PersID = PERSAB.PRIMKEY
AND WPLEVENT.VDat <= PERSAB.GLTAB
) --Missing ON clause for the INNER JOIN here
WHERE z1pers.vorname = 'henning'

SELECT and JOIN column not in Group by function

I have to join two different table to get my result.
The table 'Resource' it is simple, while the table 'Dimension.[Code]' contains, among the others, a column with different values (i.e :
Code
SILO
GRADE
OTHER 1
OTHER2
This is the reason why a join twice that column to get two different columns called GRADE and SILO.
Now, I have a query that selects the maximum value of a grade within the group as follows:
`SELECT
R.[ID] -- If I inserted that here, it is not working obviously.
-- This cannot But this is the additional column I need (see later)
DD_SILO.[Value] DIR ,
max(R.[GRADE]) GRADE_DIR
FROM [Resource] R
LEFT JOIN
Dimension DD_SILO ON R.[ID] = DD_SILO.[ID] AND DD_SILO.[Code] = 'SILO'
group by DD_SILO.[Value]'
What I need is basically to have, beside GRADE AND SILO, also the ID name, which is contained into the [Resource] table.
Please notice that [Resource].ID = [Dimension].ID
I would have solved the problem with ROW_NUMBER () to select the highest within the group, avoiding then then 'group by', but as the query has to be inserted in a bigger one, that would take too much time to run. I am using Microsoft SQL Server 2016.
Could you use a virtual table something like: -
`
select
a.max_grade_silo,
a.max_grade_value,
(select max(r.id)
from [resource] r,
[dimension] d
where r.[ID] = d.[ID] and
d.[CODE]= 'SILO' and
r.[GRADE] = a.[max_grade_value]
),
max_grade_silo a
from
(SELECT
DD_SILO.[Value] DIR ,
max(R.[GRADE]) GRADE_DIR
FROM [Resource] R
LEFT JOIN
Dimension DD_SILO ON R.[ID] = DD_SILO.[ID] AND DD_SILO.[Code] = 'SILO'
group by DD_SILO.[Value]
) temp_result (max_grade_silo, max_grade_value)
'
Probably better to look at normalizing the tables?
SELECT
MAX(R.[ID]) as ID ,
DD_SILO.[Value] DIR ,
max(R.[GRADE]) GRADE_DIR
FROM [Resource] R
LEFT JOIN
Dimension DD_SILO ON R.[ID] = DD_SILO.[ID] AND DD_SILO.[Code] = 'SILO'
group by DD_SILO.[Value]

Combining Multiple SQL Views ON Year & Month

I have a SQL Server database (2012 express) with many tables.
I have produced three different VIEWS based on different combinations of the underlying tables.
Each of these views consists of three columns, Year, Month & Total
The Total column in each of the 3 Views is of a different measure.
What I want to be able to do is to combine the three Totals into a single View
I have attempted this with the following script -
SELECT b.[Year], b.[Month], b.Fees AS [Billing],
f.Estimate AS [Estimate],
w.Fees AS [WIP]
FROM MonthlyBillingTotals AS b
FULL JOIN MonthlyFeeEstimates AS f
ON (b.[Year] = f.[Year] AND b.[Month] = f.[Month])
FULL JOIN MonthlyInstructionsWIP AS w
ON (b.[Year] = w.[Year] AND b.[Month] = w.[Month])
ORDER BY b.[Year], b.[Month]
Originally I tried INNER JOINS but of course unless the Year / Month combo existed in the first view (MonthlyBillingTotals) then it did not appear in the combined query. I therefore tried FULL JOINS, but the problem here is that I get some NULLS in the Year and Month columns, when they do not exist in the first view (MonthlyBillingTotals).
If the data in the three Views is as follows -
Then what I want is -
And even better (if it is possible) -
with the missing months filled in
You could try building the full list of Months/Years from your tables using a UNION subquery, and then use that to drive your joins.. Something like this:
SELECT a.[Year], a.[Month], b.Fees AS [Billing],
f.Estimate AS [Estimate],
w.Fees AS [WIP]
FROM (SELECT a.[Year], a.[Month] FROM MonthlyBillingTotals AS a
UNION
SELECT b.[Year], b.[Month] FROM MonthlyFeeEstimates AS b
UNION
SELECT c.[Year], c.[Month] FROM MonthlyInstructionsWIP AS c) AS a
LEFT OUTER JOIN MonthlyBillingTotals AS b
ON (a.[Year] = b.[Year] AND a.[Month] = b.[Month])
LEFT OUTER JOIN MonthlyFeeEstimates AS f
ON (a.[Year] = f.[Year] AND a.[Month] = f.[Month])
LEFT OUTER JOIN MonthlyInstructionsWIP AS w
ON (a.[Year] = w.[Year] AND a.[Month] = w.[Month])
ORDER BY a.[Year], a.[Month]
This is completely untested, but see if this solves your problems:
SELECT b.[Year], b.[Month], Coalesce(b.Fees, '0') AS [Billing],
Coalesce(f.Estimate,'0') AS [Estimate],
Coalesce(w.Fees,'0') AS [WIP]
FROM MonthlyBillingTotals AS b
LEFT JOIN MonthlyFeeEstimates AS f
ON (b.[Year] = f.[Year] AND b.[Month] = f.[Month])
LEFT JOIN MonthlyInstructionsWIP AS w
ON (b.[Year] = w.[Year] AND b.[Month] = w.[Month])
ORDER BY b.[Year], b.[Month]
The Coalesce function puts in a '0' value if nothing is found, and left joins should only join parts of MonthlyFeeEstimates and MonthlyInstructionsWIP when the year and month match.
You could set up a small date table with year and month and left join the views with that, and use the ISNULL(variable,0) function to replace NULL with 0. Another option instead of a date table would be to use a common table expression to generate a date range to join with. In any case I suggest you look up the date table (or numbers table), it can be a really useful tool.
Edit: added an example on how a date table can be created (for reference):
declare #year_month table (y int, m int)
;with cte as (
select cast('2000-01-01' as datetime) date_value
union all
select date_value + 1
from cte
where date_value + 1 < '2010-12-31'
)
insert #year_month (y, m)
select distinct year(date_value), month(date_value)
from cte
order by 1, 2
option (maxrecursion 0)
select * from #year_month

SQL Group By Clause and Empty Entries

I have a SQL Server 2005 query that I'm trying to assemble right now but I am having some difficulties.
I have a group by clause based on 5 columns: Project, Area, Name, User, Engineer.
Engineer is coming from another table and is a one to many relationship
WITH TempCTE
AS (
SELECT htce.HardwareProjectID AS ProjectId
,area.AreaId AS Area
,hs.NAME AS 'Status'
,COUNT(*) AS Amount
,MAX(htce.DateEdited) AS DateModified
,UserEditing AS LastModifiedName
,Engineer
,ROW_NUMBER() OVER (
PARTITION BY htce.HardwareProjectID
,area.AreaId
,hs.NAME
,htce.UserEditing ORDER BY htce.HardwareProjectID
,Engineer DESC
) AS row
FROM HardwareTestCase_Execution AS htce
INNER JOIN HardwareTestCase AS htc ON htce.HardwareTestCaseID = htc.HardwareTestCaseID
INNER JOIN HardwareTestGroup AS htg ON htc.HardwareTestGroupID = htg.HardwareTestGroupId
INNER JOIN Block AS b ON b.BlockId = htg.BlockId
INNER JOIN Area ON b.AreaId = Area.AreaId
INNER JOIN HardwareStatus AS hs ON htce.HardwareStatusID = hs.HardwareStatusId
INNER JOIN j_Project_Testcase AS jptc ON htce.HardwareProjectID = jptc.HardwareProjectId AND htce.HardwareTestCaseID = jptc.TestcaseId
WHERE (htce.DateEdited > #LastDateModified)
GROUP BY htce.HardwareProjectID
,area.AreaId
,hs.NAME
,htce.UserEditing
,jptc.Engineer
)
The gist of what I want is to be able to deal with empty Engineer columns. I don't want this column to have a blank second entry (where row=2).
What I want to do:
Group the items with "row" value of 1 & 2 together.
Select the Engineer that isn't empty.
Do not deselect engineers where there is not a matching row=2.
I've tried a series of joins to try and make things work. No luck so far.
Use j_Project_Testcase PIVOT( MAX(Engineer) for Row in ( [1], [2] ) then select ISNULL( [1],[2]) to select the Engineer value
I can give you a more robust example if you set up a SQL fiddle
Try reading this: PIVOT and UNPIVOT