Add count() to pivot table with totals - sql

I have the following query: (simplified to one year)
WITH myTable
AS (SELECT cv.Company,
cv.Zip,
DATEPART(year, od.OrderDate) AS TheYear,
'' + DATEPART(year, od.OrderDate) AS TheYear2,
od.OrderNumber AS countOrders,
STUFF(
(
SELECT '| ' + FORMAT(DateVisited, 'MM-yyyy') + ' ' + MeetingType + ' '
FROM CustomerVisits cv1
WHERE(RIGHT(od.Email, LEN(od.Email) - CHARINDEX('#', od.email))) = cv1.EmailDomain
AND cv1.Zip = od.Zip
GROUP BY MeetingType,
FORMAT(DateVisited, 'MM-yyyy')
ORDER BY FORMAT(DateVisited, 'MM-yyyy') ASC FOR XML PATH('')
), 1, 1, '') AS Meetings,
od.FinalProductTotal AS total
FROM orders od
LEFT JOIN CustomerVisits cv ON od.Zip = cv.Zip
WHERE(RIGHT(od.Email, LEN(od.Email) - CHARINDEX('#', od.email))) = cv.EmailDomain
--AND od.OrderDate > #fromDate
AND approved = 1
AND cancelled = 0
AND od.OrderDate > '01-JAN-2010')
--and the PIVOT
SELECT Company,
Zip,
Meetings,
[02020] AS [Orders - 2020],
ISNULL(CAST([2020] AS INT), 0) AS [Total - 2020],
Total = CAST((SUM(ISNULL([2020], 0))) AS INT)
FROM myTable PIVOT(COUNT(countOrders) FOR TheYear2 IN([02020])) AS myPvt PIVOT(SUM(total) FOR TheYear IN([2020])) AS myPvt2
GROUP BY Company,
zip,
Meetings,
[2020],
[02020];
For it I'm trying to add the count of orders for each year, but I'm having problems understanding how should I proceed, I might be complicating it too much, but I'm not getting the proper count
I think that the myTable table is getting properly the "countOrders" but I'm not sure if I'm doing it properly on the pivot table to group by them and show the count by year
right now it kinda seems that is counting all the orders as I needed, however when I manually check on the DB seems that the data is off.
also, something I don't understand is why is it repeating the "company" so many times when I'm doing grouping by it?
example

I ended up case instead of pivot table. this solved the issue, with format:
CAST(SUM(CASE WHEN TheYear = 2010 THEN 1 ELSE 0 END)AS INT) AS [Orders - 2010] ,
CAST(SUM(CASE WHEN TheYear = 2010 THEN Total ELSE 0 END)AS INT) AS [Total - 2010],
in case it works for someone :)

Related

how to perform division with multiple conditions in a group SSRS

How can I calculate change Year\Month over Year\Month for the past 4 years when Year and Month in a group in SSRS.
I need like that:
Row 2016 vs 2015 % Change is a division of January 2016 Premium/ January 2015 Premium and so on
Something like CASE WHEN year=2016 and month = 1 then 2016 premium/2015 premium
I am trying to:
IIF(Fields!YearNum.Value=2016 and Fields!MonthNum.Value=1, Fields.Premium.Value/IIF(Fields!YearNum.Value=2015 and Fields!MonthNum.Value=1,Fields.Premium.Value,1),Nothing)
My query looks like that:
SELECT b.YearNum,
b.MonthNum,
b.MonthName,
SUM(Premium) as Premium,
ISNULL(sum(case when TransactionType IN ('Policy', 'Reinstatement') then 1 ELSE 0 END),0) as Bound,
FROM tblCalendar b
LEFT JOIN Test_Plaza_ProductionReport a ON b.MonthNum = MONTH(a.EffectiveDate) AND b.YearNum=YEAR(a.EffectiveDate)
WHERE YEAR(EffectiveDate) <> 2017
GROUP BY b.YearNum,
b.MonthNum,
b.MonthName
it seems you are using matrix so you don't need to specify the month
try expression like:
=(sum(IIF(Fields!YearNum.Value=2016, Fields.Premium.Value,0)) - sum(IIF(Fields!YearNum.Value=2015, Fields.Premium.Value,0)))
/sum(IIF(Fields!YearNum.Value=2016, Fields.Premium.Value,0))
Setup:
To mimic your setup, I manually created the following table (dbo.Unpivoted):
Pivot Query:
Next, I pivoted the data to yield the following result:
SELECT [Year] AS [Year], [January], [February]
FROM
(SELECT * FROM dbo.Unpivoted) src
PIVOT
(
Sum(src.Premium)
FOR src.[Month] IN ("January", "February")
) as PivotTable
ORDER BY [Year] DESC;
Self-Join Query:
Based on the above results (now called dbo.Premium), I used a self-join to calculate the year-over-year percentage increase:
Select Cast(upyear.[Year] as varchar) + ' vs ' + Cast(downyear.[Year] as varchar) + ' % Change' [Year],(upyear.January/downyear.January - 1) January, (upyear.February/downyear.February - 1) February
From dbo.Premium upyear
INNER JOIN
dbo.Premium downyear
ON upyear.[Year] = downyear.[Year] + 1;
Update: The combined query
CREATE TABLE #Combined([Year] VARCHAR(50), January FLOAT, February FLOAT)
INSERT INTO #Combined SELECT CAST([Year] AS varchar) AS [Year], [January], [February]
FROM
(SELECT * FROM dbo.Unpivoted) src
PIVOT
(
Sum(src.Premium)
FOR src.[Month] IN ("January", "February")
) as PivotTable;
INSERT INTO #Combined SELECT CAST(upyear.[Year] AS VARCHAR) + ' vs ' + CAST(downyear.[Year] as VARCHAR) + ' % Change' [Year],(upyear.January/downyear.January - 1) January, (upyear.February/downyear.February - 1) February
From
#Combined upyear
INNER JOIN
#Combined downyear
ON upyear.[Year] = downyear.[Year] + 1;
SELECT * FROM #Combined
ORDER BY
CASE
WHEN LEN([Year]) = 4 THEN 1
ELSE 0
END DESC,
[Year] DESC;
Begin
Drop Table #Combined
End

Select statement by group by total

I have two tables Contact and Invoice linked by ContactId
Please see fiddle
I am selecting all the contact who have spend more than 2500 per any year and the query works fine.
I wanted it to display in a below format.
Any help on this please using sql-server. I can easily do this using crystal-report cross tab, but trying to do thing only using sql-server
You can PIVOT, then UNPIVOT your data from your original query to get it in the desired format:
WITH T AS
( SELECT c.ContactID,
ContactName = c.Name,
Year = DATEPART(YEAR, i.InvDate),
Invoices = CAST(COUNT(i.InvoiceID) AS FLOAT),
InvTotal = CAST(SUM(i.InvTotal) AS FLOAT)
FROM Invoice AS i
INNER JOIN dbo.Contact AS c
ON c.ContactID = i.InvContactID
GROUP BY c.ContactID, c.Name, DATEPART(YEAR, i.InvDate)
HAVING SUM(i.InvTotal) > 2000
)
SELECT ContactName = CASE WHEN pvt.Measure = 'InvTotal' THEN '' ELSE pvt.ContactName END,
pvt.Measure,
[2012] = ISNULL(pvt.[2012], 0),
[2013] = ISNULL(pvt.[2013], 0),
[2014] = ISNULL(pvt.[2014], 0)
FROM T
UNPIVOT
( Value
FOR Measure IN ([Invoices], [InvTotal])
) AS upvt
PIVOT
( SUM(Value)
FOR [Year] IN ([2012], [2013], [2014])
) AS pvt
ORDER BY pvt.ContactName, Measure;
Example on SQL Fiddle

Using SELECT result in another SELECT

So here is my query
SELECT
*
FROM
Score AS NewScores
WHERE
InsertedDate >= DATEADD(mm, -3, GETDATE());
SELECT
ROW_NUMBER() OVER( ORDER BY NETT) AS Rank,
Name,
FlagImg,
Nett,
Rounds
FROM (
SELECT
Members.FirstName + ' ' + Members.LastName AS Name,
CASE
WHEN MenuCountry.ImgURL IS NULL THEN
'~/images/flags/ismygolf.png'
ELSE
MenuCountry.ImgURL
END AS FlagImg,
AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett,
COUNT(Score.ScoreID) AS Rounds
FROM
Members
INNER JOIN
Score
ON Members.MemberID = Score.MemberID
LEFT OUTER JOIN MenuCountry
ON Members.Country = MenuCountry.ID
WHERE
Members.Status = 1
GROUP BY
Members.FirstName + ' ' + Members.LastName,
MenuCountry.ImgURL
) AS Dertbl
ORDER BY;
The query is to give a result set for a GridView based leaderboard and what I want is to only get the average of Scores that are less than 3 months old. I have this in 2 parts as you can see and obviously it gives an error like this.
Msg 4104, Level 16, State 1, Line 2
The multi-part identifier "NewScores.NetScore" could not be bound.
Which is because of this AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett
How do I make it so that I can use NewScores there so I'm only getting the average of the scores less than 3 months old?
EDIT: Using the answers people provided I've solved it by using a join in the correct place and here is the correct query:
SELECT ROW_NUMBER() OVER(ORDER BY NETT) AS Rank, Name, FlagImg, Nett, Rounds FROM (SELECT Members.FirstName + ' ' + Members.LastName AS Name, CASE WHEN MenuCountry.ImgURL IS NULL THEN '~/images/flags/ismygolf.png' ELSE MenuCountry.ImgURL END AS FlagImg, AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett, COUNT(NewScores.ScoreID) AS Rounds FROM Members INNER JOIN (SELECT * FROM Score WHERE InsertedDate >= DATEADD(mm, -5, GETDATE())) NewScores ON Members.MemberID = NewScores.MemberID LEFT OUTER JOIN MenuCountry ON Members.Country = MenuCountry.ID WHERE Members.Status = 1 GROUP BY Members.FirstName + ' ' + Members.LastName, MenuCountry.ImgURL) AS Dertbl ORDER BY Nett ASC
NewScores is an alias to Scores table - it looks like you can combine the queries as follows:
SELECT
ROW_NUMBER() OVER( ORDER BY NETT) AS Rank,
Name,
FlagImg,
Nett,
Rounds
FROM (
SELECT
Members.FirstName + ' ' + Members.LastName AS Name,
CASE
WHEN MenuCountry.ImgURL IS NULL THEN
'~/images/flags/ismygolf.png'
ELSE
MenuCountry.ImgURL
END AS FlagImg,
AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett,
COUNT(Score.ScoreID) AS Rounds
FROM
Members
INNER JOIN
Score NewScores
ON Members.MemberID = NewScores.MemberID
LEFT OUTER JOIN MenuCountry
ON Members.Country = MenuCountry.ID
WHERE
Members.Status = 1
AND NewScores.InsertedDate >= DATEADD(mm, -3, GETDATE())
GROUP BY
Members.FirstName + ' ' + Members.LastName,
MenuCountry.ImgURL
) AS Dertbl
ORDER BY;
What you are looking for is a query with WITH clause, if your dbms supports it. Then
WITH NewScores AS (
SELECT *
FROM Score
WHERE InsertedDate >= DATEADD(mm, -3, GETDATE())
)
SELECT
<and the rest of your query>
;
Note that there is no ; in the first half. HTH.
You are missing table NewScores, so it can't be found. Just join this table.
If you really want to avoid joining it directly you can replace NewScores.NetScore with SELECT NetScore FROM NewScores WHERE {conditions on which they should be matched}

Golf Scoring SQL Query

I have the following MS SQL query which cross references three tables, (tTeam, tPlayer and tScores) to get the total "Net score", "Gross score" and "Position" ordered by Net score and Team.
SELECT TeamID, Team, NetScore, Gross,
CASE WHEN cnt > 1 THEN 'T' + CAST(rnk AS VARCHAR(5))
ELSE CAST(rnk AS VARCHAR(5))
END Pos
FROM (
SELECT tTeam.TeamID,
tTeam.Title AS Team,
SUM(CONVERT(INT, tScores.Net_Score)) AS NetScore,
SUM(CONVERT(INT, tScores.Out_Score) + CONVERT(int, tScores.In_Score)) AS Gross,
rank() OVER ( ORDER BY SUM(CONVERT(INT, tScores.Net_Score))) rnk,
COUNT(*) OVER ( PARTITION BY SUM(CONVERT(INT, tScores.Net_Score))) cnt
FROM tScores INNER JOIN tPlayer ON tScores.PlayerID = tPlayer.PlayerID INNER JOIN tTeam ON tPlayer.TeamID = tTeam.TeamID
WHERE tTeam.TournamentID = 13
GROUP BY tTeam.TeamID, tTeam.Title ) temp
ORDER BY NetScore, Team
The query works great but (and here is where i need some help), it is calculating all of the players Net and Gross scores by team when all I need it to do is calculate the "4 lowest Player's Net and Gross Scores" by team only.
I have spent the last day and a half pulling my hair out with this one and any help will be greatly appreciated.
Thanks in advance!
If I understood correctly that you want to sum four lowest scores per player only, you might use another set of row_numbers to isolate lowest scores. I don't think that rn_gross is necessary (based on rank() function) but I included it nevertheless. If there is no need for separate numbering remove conditionals from sums and add and lowestScores.rn_net <= 4 to where clause.
; with lowestScores as
(
select *,
ROW_NUMBER() over (PARTITION by PlayerID
order by CONVERT(INT, Net_Score)) rn_net,
ROW_NUMBER() over (PARTITION by PlayerID
order by CONVERT(INT, Net_Score) + CONVERT(int,In_Score)) rn_gross
from tScores
),
temp as
(
SELECT tTeam.TeamID,
tTeam.Title AS Team,
SUM(CASE WHEN rn_net <= 4 THEN CONVERT(INT, lowestScores.Net_Score) END) AS NetScore,
SUM(CASE WHEN rn_gross <= 4 THEN CONVERT(INT, lowestScores.Out_Score) END
+ CASE WHEN rn_gross <= 4 THEN CONVERT(int, lowestScores.In_Score) END) AS Gross,
rank() OVER ( ORDER BY SUM(CASE WHEN rn_net <= 4 THEN CONVERT(INT, lowestScores.Net_Score) END)) rnk,
COUNT(*) OVER ( PARTITION BY SUM(CASE WHEN rn_net <= 4 THEN CONVERT(INT, lowestScores.Net_Score) END)) cnt
FROM lowestScores
INNER JOIN tPlayer
ON lowestScores.PlayerID = tPlayer.PlayerID
INNER JOIN tTeam
ON tPlayer.TeamID = tTeam.TeamID
WHERE tTeam.TournamentID = 13
GROUP BY tTeam.TeamID, tTeam.Title
)
SELECT TeamID, Team, NetScore, Gross,
CASE WHEN cnt > 1
THEN 'T' + CAST(rnk AS VARCHAR(5))
ELSE CAST(rnk AS VARCHAR(5))
END Pos
FROM temp
ORDER BY NetScore, Team

insert 0 into successive row with same fields value

I have a table containing the following line:
I'd like to create a view that displays the following result (without changing my original table) :
For each line having the same id,day,month and year I'd like to leave a single line with the cost and count and insert 0 in the others.
Here is a portable approach not requiring PARTITION. I have assumed you will not have the same datetimeIN value for more than one row in a group:
select t.id, t.day, t.month, t.year,
case when tm.id is null then 0 else t.cost end as cost,
case when tm.id is null then 0 else t.Count end as Count,
t.datetimeIN, t.datetimeOUT
from MyTable t
left outer join (
select id, day, month, year, min(datetimeIN) as minIN
from MyTable
group by id, day, month, year
) tm on t.id = tm.id
and t.day = tm.day
and t.month = tm.month
and t.year = tm.year
and t.datetimeIN = tm.minIN
You can do something like this:
SELECT id, day, month, year,
CASE WHEN nNum = 1 then cost else 0 end as cost,
CASE WHEN nNum = 1 then "Count" else 0 end as "Count",
datetimeIN, datetimeOUT
FROM (
SELECT id, day, month, year,
cost, "Count", datetimeIN, datetimeOUT,
row_number() OVER (PARTITION BY id, day, month, year
ORDER BY datetimeIN) as nNum
FROM TableName
) A
It uses row_number() to number the rows, and then a CASE statement to single out the first one and make it behave differently.
See it working on SQL Fiddle here.
or, using a common table expression:
with commonTableExp ([day], [month], [year], minDate) as (
select [day], [month], [year], min(datetimeIn)
from #temp
group by [day], [month], [year])
select id,
dt.[day],
dt.[month],
dt.[year],
case when datetimein = minDate then [cost] else 0 end,
case when datetimein = minDate then [count] else 0 end,
dateTimeIn
from #temp dt join commonTableExp cte on
dt.[day] = cte.[day] and
dt.[month] = cte.[month] and
dt.[year] = cte.[year]
order by dateTimeIn
Query
Select id, [day], [month], [year], Case When K.RowID = 1 Then [cost] Else 0 End as Cost, Case When K.RowID = 1 Then [count] Else 0 End as [count], [DateTimeIN], [DateTimeOut] From
(
select ROW_NUMBER() Over(Partition by id, [day], [month], [year] Order by ID ) as RowID, * From Testing
)K
Drop table Testing
Click here to see SQL Profiler details for Red Filter's Query
Click here to see SQL Profiler details for my Query
For More Information You can see SQL Fiddle