User-defined parameters in stored procedure are not calculating data - tsql

I am creating a stored procedure for an SSRS report and normally when I have a multi-valued parameter, I use a split function to separate and calculate the results.
But my issue with this stored procedure is that I have three additional values I need to add to the data set; which I have done here:
CREATE PROCEDURE dbo.rpt_DataSet_AgentClassType
AS
BEGIN
SET NOCOUNT ON;
SELECT
x.ID, x.ClassType
FROM
(SELECT 997 AS [ID], 'Desktops' AS [ClassType]
UNION ALL
SELECT 998 AS [ID], 'Mobile Devices' AS [ClassType]
UNION ALL
SELECT 999 AS [ID], 'Mobile Apps' AS [ClassType]
UNION ALL
SELECT ACT.[id] AS [ID], ACT.[description] AS [ClassType]
FROM dbo.AgentClassTypes AS [ACT]
WHERE ACT.id NOT IN (14, 15, 16)
AND ACT.active = 1
) AS x
ORDER BY
x.ID;
END;
The resulting data set looks like:
ID ClassType
--------------------------------
1 TWRA Central License Sales
3 General Agent
4 Service Desk
5 Internet
....
18 State Parks
19 TWRA Special
997 Desktops
998 Mobile Devices
999 Mobile Apps
What I am trying to accomplish here is that if the user selects 997 - Desktops, 998 - Mobile Devices, or 999 - Mobiles Apps; the stored procedure should not use the split function since the value does not exist in the table, but search a specific field in a table for the data set.
Here is my stored procedure:
ALTER PROCEDURE dbo.rpt_HarvestByAgentType
#StartDate DATE,
#EndDate DATE,
#AgentClassType VARCHAR(MAX),
#SpeciesID VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#HarvestByAgentTypeResults') IS NOT NULL
DROP TABLE #HarvestByAgentTypeResults;
CREATE TABLE #HarvestByAgentTypeResults
(
TotalHarvest INT,
HarvestDate DATE,
AgentClassType VARCHAR(100),
SpeciesID INT,
SpeciesType VARCHAR(50)
);
-- Desktops
IF #AgentClassType IN ('997')
BEGIN
INSERT INTO #HarvestByAgentTypeResults (TotalHarvest, HarvestDate, AgentClassType, SpeciesID, SpeciesType)
SELECT
COUNT(x.HarvestRecordID) AS [TotalHarvest],
x.HarvestDate AS [HarvestDate],
'Desktop' AS [AgentClassType],
x.SpeciesID AS [SpeciesID],
x.SpeciesType AS [SpeciesType]
FROM
(SELECT
CHR.HarvestRecordID,
CAST(CHR.HarvestDate AS DATE) AS [HarvestDate],
CHR.SpeciesID AS [SpeciesID],
HST.SpeciesType AS SpeciesType
FROM
dbo.CustomerHarvestReports AS [CHR]
INNER JOIN
dbo.Agents AS [A] ON A.agentID = CHR.agentID
INNER JOIN
dbo.AgentClassTypes AS [ACT] ON ACT.id = A.agentClassTypeID
INNER JOIN
dbo.HarvestSpeciesTypes AS [HST] ON HST.SpeciesID = CHR.SpeciesID AND HST.InActive = 0
WHERE
CHR.HarvestDate >= #StartDate
AND CHR.HarvestDate <= #EndDate
AND HST.SpeciesID In (SELECT Item
FROM dbo.fnSplit(#SpeciesID,','))
AND CHR.isInvalidated = 0
AND ((CHR.httpUserAgentString LIKE '%Win%' AND CHR.httpUserAgentString NOT LIKE '%Windows Phone%' ) -- Windows Machines excluding Windows phones
OR (CHR.httpUserAgentString LIKE '%Linux%' AND CHR.httpUserAgentString NOT LIKE '%Android') -- Linux machines excluding Android phones
OR CHR.httpUserAgentString LIKE '%Macintosh%' -- Mac books
OR CHR.httpUserAgentString LIKE '%CrOS%') -- Chrome Books
) x
GROUP BY
x.HarvestDate, x.SpeciesID, x.SpeciesType
END;
-- Mobile Devices
ELSE IF #AgentClassType IN ('998')
BEGIN
INSERT INTO #HarvestByAgentTypeResults(TotalHarvest, HarvestDate, AgentClassType, SpeciesID, SpeciesType)
SELECT
COUNT(x.HarvestRecordID) AS [TotalHarvest],
x.HarvestDate AS [HarvestDate],
'MobileDevice' AS [AgentClassType] ,
x.SpeciesID AS [SpeciesID] ,
x.SpeciesType AS [SpeciesType]
FROM ( SELECT CHR.HarvestRecordID ,
CAST(CHR.HarvestDate AS DATE) AS [HarvestDate] ,
ACT.[description] agentClass ,
CHR.SpeciesID AS [SpeciesID] ,
HST.SpeciesType AS SpeciesType
FROM dbo.CustomerHarvestReports AS [CHR]
LEFT OUTER JOIN dbo.Agents AS [A] ON A.agentID = CHR.agentID
LEFT OUTER JOIN dbo.AgentClassTypes AS [ACT] ON ACT.id = A.agentClassTypeID
LEFT OUTER JOIN dbo.HarvestSpeciesTypes AS [HST] ON HST.SpeciesID = CHR.SpeciesID AND HST.InActive = 0
WHERE CHR.HarvestDate >= #StartDate
AND CHR.HarvestDate <= #EndDate
AND HST.SpeciesID In ( SELECT Item FROM dbo.fnSplit(#SpeciesID,','))
AND CHR.isInvalidated = 0
AND (( httpUserAgentString LIKE '%iPhone%' AND httpUserAgentString LIKE '%Version%' ) -- iPhones
OR ( httpUserAgentString LIKE '%Android%' AND httpUserAgentString NOT LIKE '%Version%' ) -- Androids
OR ( httpUserAgentString LIKE '%iPad%' AND httpUserAgentString LIKE '%Version%' ) -- iPads
OR ( httpUserAgentString LIKE '%BB10%' ) -- Blackberries
OR ( httpUserAgentString LIKE '%Windows Phone%' )) -- Windows Phones
) x
GROUP BY x.HarvestDate ,
x.SpeciesID ,
x.SpeciesType
END;
-- Mobile Apps
ELSE IF #AgentClassType IN ('999')
BEGIN
INSERT INTO #HarvestByAgentTypeResults
( TotalHarvest ,
HarvestDate ,
AgentClassType ,
SpeciesID ,
SpeciesType
)
SELECT COUNT(x.HarvestRecordID) AS [TotalHarvest] ,
x.HarvestDate AS [HarvestDate],
'MobileApp' AS [AgentClassType] ,
x.SpeciesID AS [SpeciesID] ,
x.SpeciesType AS [SpeciesType]
FROM ( SELECT CHR.HarvestRecordID ,
CAST(CHR.HarvestDate AS DATE) AS [HarvestDate] ,
ACT.[description] agentClass ,
CHR.SpeciesID AS [SpeciesID] ,
HST.SpeciesType AS SpeciesType
FROM dbo.CustomerHarvestReports AS [CHR]
LEFT OUTER JOIN dbo.Agents AS [A] ON A.agentID = CHR.agentID
LEFT OUTER JOIN dbo.AgentClassTypes AS [ACT] ON ACT.id = A.agentClassTypeID
LEFT OUTER JOIN dbo.HarvestSpeciesTypes AS [HST] ON HST.SpeciesID = CHR.SpeciesID AND HST.InActive = 0
WHERE CHR.HarvestDate >= #StartDate
AND CHR.HarvestDate <= #EndDate
AND HST.SpeciesID In ( SELECT Item FROM dbo.fnSplit(#SpeciesID,','))
AND CHR.isInvalidated = 0
AND (( httpUserAgentString LIKE '%iPhone%' AND httpUserAgentString NOT LIKE '%Version%' AND httpUserAgentString NOT LIKE '%Windows Phone%' ) -- iPhones
OR ( httpUserAgentString LIKE '%Android%' AND httpUserAgentString LIKE '%Version%' )) -- Androids
) x
GROUP BY x.HarvestDate ,
x.SpeciesID ,
x.SpeciesType
END;
ELSE IF (#AgentClassType NOT IN ('997','998','999'))
BEGIN
INSERT INTO #HarvestByAgentTypeResults
( TotalHarvest ,
HarvestDate ,
AgentClassType ,
SpeciesID ,
SpeciesType
)
SELECT COUNT(x.HarvestRecordID) AS [TotalHarvest] ,
x.HarvestDate AS [HarvestDate] ,
x.AgentTypeClass AS [AgentTypeClass] ,
x.SpeciesID AS [SpeciesID] ,
x.SpeciesType AS [SpeciesType]
FROM ( SELECT CHR.HarvestRecordID ,
CAST(CHR.HarvestDate AS DATE) AS [HarvestDate] ,
ACT.[description] AS [AgentTypeClass] ,
CHR.SpeciesID AS [SpeciesID] ,
HST.SpeciesType AS [SpeciesType]
FROM dbo.CustomerHarvestReports AS [CHR]
INNER JOIN dbo.Agents AS [A] ON A.agentID = CHR.agentID
INNER JOIN dbo.AgentClassTypes AS [ACT] ON ACT.id = A.agentClassTypeID
INNER JOIN dbo.HarvestSpeciesTypes AS [HST] ON HST.SpeciesID = CHR.SpeciesID AND HST.InActive = 0
WHERE CHR.HarvestDate >= #StartDate
AND CHR.HarvestDate <= #EndDate
AND HST.SpeciesID IN ( SELECT Item FROM dbo.fnSplit(#SpeciesID, ','))
AND A.agentClassTypeID IN ( SELECT Item FROM dbo.fnSplit(#AgentClassType ,','))
AND CHR.isInvalidated = 0
) AS x
GROUP BY x.HarvestDate ,
x.AgentTypeClass ,
x.SpeciesID ,
x.SpeciesType
ORDER BY x.HarvestDate ,
x.AgentTypeClass,
x.SpeciesType;
END
SELECT TotalHarvest ,
HarvestDate ,
AgentClassType ,
SpeciesID ,
SpeciesType FROM #HarvestByAgentTypeResults
ORDER BY HarvestDate, SpeciesType, AgentClassType;
DROP TABLE #HarvestByAgentTypeResults;
END;
When I execute the stored procedure from Management Studio, if I only input 997 or 998 or 999 in the #AgentClasstype variable, I get results. But if I include values such as 1, 3, 5, etc... - then the data set I would expect from 997, 998, or 999 are not returned.
Hopefully I have explained this clearly - it has been difficult to script correctly and seemingly - just as hard to write up for help.

Related

Can I insert a dynamic number of rows into a table using values from the table?

I want to insert a dynamic number of rows into a table, based on information in that table.
I can do it using the code below, but I'm wondering if there's a way to avoid the loop.
The commented out section was my best attempt at what I was trying to do, but it gave me an error of:
"The reference to column "iCount" is not allowed in an argument to a TOP, OFFSET, or FETCH clause. Only references to columns at an outer scope or standalone expressions and subqueries are allowed here."
DECLARE #TableX TABLE (
TDate DATE
, TType INT
, Fruit NVARCHAR(20)
, Vegetable NVARCHAR(20)
, Meat NVARCHAR(20)
, Bread NVARCHAR(20)
)
INSERT INTO #TableX VALUES
('2016-11-10',1,'Apple','Artichoke',NULL,NULL)
, ('2016-11-10',1,'Banana','Beet',NULL,NULL)
, ('2016-11-10',1,'Canteloupe','Cauliflower',NULL,NULL)
, ('2016-11-10',1,'Durian','Daikon',NULL,NULL)
, ('2016-11-10',2,NULL,NULL,'Rabbit','Rye')
, ('2016-11-10',2,NULL,NULL,'Sausage','Sourdough')
, ('2016-11-11',1,'Elderberry','Eggplant',NULL,NULL)
, ('2016-11-11',2,NULL,NULL,'Turkey','Tortilla')
, ('2016-11-11',2,NULL,NULL,'Venison','Vienna')
SELECT * FROM #TableX
DECLARE #BlankRow TABLE (
ID INT IDENTITY
, TDate DATE
, TType INT
, iCount INT
)
DECLARE #Counter1 INT = 0
, #RowCount INT
; WITH BR1
AS (
SELECT TDate, TType, COUNT(*) AS iCount
FROM #TableX
WHERE TType = 1
GROUP BY TDate, TType
)
, BR2
AS (
SELECT TDate, TType, COUNT(*) AS iCount
FROM #TableX
WHERE TType = 2
GROUP BY TDate, TType
)
INSERT INTO #BlankRow
SELECT ISNULL(BR1.TDate, BR2.TDate) AS TDate,
CASE WHEN ISNULL(BR1.iCount,0) < ISNULL(BR2.iCount,0) THEN 1 ELSE 2 END AS TType,
ABS(ISNULL(BR1.iCount,0) - ISNULL(BR2.iCount,0)) AS iCount
FROM BR1
FULL JOIN BR2
ON BR1.TDate = BR2.TDate
WHILE #Counter1 < (SELECT MAX(ID) FROM #BlankRow)
BEGIN
SET #Counter1 += 1
SET #RowCount = (SELECT iCount FROM #BlankRow WHERE ID = #Counter1)
INSERT INTO #TableX
SELECT TOP (#RowCount) tx.TDate, br.TType, NULL, NULL, NULL, NULL
FROM #TableX tx
LEFT JOIN #BlankRow br
ON tx.TDate = br.TDate
WHERE br.ID = #Counter1
END
/*INSERT INTO #TableX
SELECT TOP (tx.iCount) tx.TDate, br.TType, NULL, NULL, NULL, NULL
FROM #TableX tx
JOIN #BlankRow br
ON tx.TDate = br.TDate*/
SELECT *
FROM #TableX
ORDER BY TDate, TType,
ISNULL(Fruit,REPLICATE(CHAR(255),20)),
ISNULL(Vegetable,REPLICATE(CHAR(255),20)),
ISNULL(Meat,REPLICATE(CHAR(255),20)),
ISNULL(Bread,REPLICATE(CHAR(255),20))
The data is silly, I know, but my end goal is to have two different Tablix's in ReportBuilder that end up with the same number of rows so the headers of my groups show up at the same place on the page.
Something like this:
declare #TableX table(TDate date
,TType int
,Fruit nvarchar(20)
,Vegetable nvarchar(20)
,Meat nvarchar(20)
,Bread nvarchar(20)
);
insert into #TableX values
('2016-11-10',1,'Apple','Artichoke',NULL,NULL)
,('2016-11-10',1,'Banana','Beet',NULL,NULL)
,('2016-11-10',1,'Canteloupe','Cauliflower',NULL,NULL)
,('2016-11-10',1,'Durian','Daikon',NULL,NULL)
,('2016-11-10',2,NULL,NULL,'Rabbit','Rye')
,('2016-11-10',2,NULL,NULL,'Sausage','Sourdough')
,('2016-11-11',1,'Elderberry','Eggplant',NULL,NULL)
,('2016-11-11',2,NULL,NULL,'Turkey','Tortilla')
,('2016-11-11',2,NULL,NULL,'Venison','Vienna');
with DataRN as
(
select *
,row_number() over (partition by TDate, TType order by TDate) rn
from #TableX
)
,RowsRN as
(
select tt.TDate
,tt.TType
,td.rn
from (select distinct TDate, TType
from #TableX
) tt
full join (select distinct t1.TDate
,row_number() over (partition by t1.TDate, t1.TType order by t1.TDate) rn
from #TableX t1
) td
on(tt.TDate = td.TDate)
)
select r.TDate
,r.TType
,d.Fruit
,d.Vegetable
,d.Meat
,d.Bread
from DataRN d
full join RowsRN r
on(d.TDate = r.TDate
and d.TType = r.TType
and d.rn = r.rn
)
order by r.TDate
,r.TType
,isnull(d.Fruit,REPLICATE(CHAR(255),20))
,isnull(d.Vegetable,REPLICATE(CHAR(255),20))
,isnull(d.Meat,REPLICATE(CHAR(255),20))
,isnull(d.Bread,REPLICATE(CHAR(255),20))
In response to your comment, here is how you would use another cte to generate the full list of dates that you would need, if you havn't got a Dates reference table already (These are tremendously useful):
declare #MinDate date = (select min(TDate) from #TableX);
declare #MaxDate date = (select max(TDate) from #TableX);
with Dates as
(
select #MinDate as DateValue
union all
select dateadd(d,1,DateValue)
from Dates
where DateValue < #MaxDate
)
select DateValue
from Dates
option (maxrecursion 0);

Query to find duplicate rows in a table

I am running the following query which is terribly inefficient and can take hours. I am having SQL brain farts today and I do not know how to improve this query. There are several nullable varchar fields, and I need to identify the duplicate rows (all columns containing identical values as another row)
select * from transactions x where exists (
select Coalesce(ColA, ''),
Coalesce(ColB, ''),
Coalesce(ColC, '')
from transactions y
where Coalesce(x.ColA, '') = Coalesce(x.ColA, '') and
Coalesce(x.ColB, '') = Coalesce(x.ColB, '') and
Coalesce(x.ColC, '') = Coalesce(x.ColC, '')
group by Coalesce(ColA, ''),
Coalesce(ColB, ''),
Coalesce(ColC, '')
having count(*) > 1
)
Why does this take so long to run? There has to be a better way.
You could improve it by
removing unnecesssary checks
putting a composite index on ColA, ColB and ColC
What is unnecessary? It seems to be unnecessary to join the table with itself. Why don't you use a simple GROUP BY? You also don't need the WHERE:
SELECT COALESCE(ColA, '') AS ColA,
COALESCE(ColB, '') AS ColB,
COALESCE(ColC, '') AS ColC,
Count(*) As Cnt
FROM transactions t
GROUP BY COALESCE(ColA, ''), COALESCE(ColB, ''), COALESCE(ColC, '')
HAVING Count(*) > 1
Does this work?
DECLARE #transactions TABLE (
ColA INT
, ColB INT
, ColC INT
, ColD INT
, ColE INT
, ColF INT
)
DECLARE #Counter1 INT = 0
WHILE #Counter1 < 10000
BEGIN
SET #Counter1 += 1
INSERT INTO #transactions
SELECT ROUND(RAND()*10,0)
, ROUND(RAND()*10,0)
, ROUND(RAND()*10,0)
, ROUND(RAND()*10,0)
, ROUND(RAND()*10,0)
, ROUND(RAND()*10,0)
END
;WITH Dupe
AS (
SELECT *, ROW_NUMBER() OVER
(PARTITION BY ColA, ColB, ColC, ColD, ColE, ColF
ORDER BY ColA, ColB, ColC, ColD, ColE, ColF) AS rn
FROM #transactions
)
SELECT * FROM Dupe WHERE rn > 1
You can use an ISNULL on anything where you need to compare a value that might be null. Note that most of this I've written is just to generate a useful data set. With 6 columns and 10,000 rows I got 42 identical rows in less than a second. No triples. Bumped it up to 100,000 rows and I got 3,489 duplicate rows, including some triples. Took 3 seconds.
Here's an example using text. This whole thing took 25 seconds on 100,000 records, although my timer shows that less than 4 of that was finding the duplicates, with the remainder being the table population.
DECLARE #transactions2 TABLE (
ColA NVARCHAR(30)
, ColB NVARCHAR(30)
, ColC NVARCHAR(30)
, ColD NVARCHAR(30)
, ColE NVARCHAR(30)
, ColF NVARCHAR(30)
)
DECLARE #names TABLE (
ID INT IDENTITY
, Name NVARCHAR(30)
)
DECLARE #Counter2 INT = 0
, #ColA NVARCHAR(30)
, #ColB NVARCHAR(30)
, #ColC NVARCHAR(30)
, #ColD NVARCHAR(30)
, #ColE NVARCHAR(30)
, #ColF NVARCHAR(30)
INSERT INTO #names VALUES
('Anderson, Arthur')
, ('Broberg, Bruce')
, ('Chan, Charles')
, ('Davidson, Darwin')
, ('Eggert, Emily')
, ('Fox, Francesca')
, ('Garbo, Greta')
, ('Hollande, Hortense')
, ('Iguadolla, Ignacio')
, ('Jackson, Jurimbo')
, ('Katana, Ken')
, ('Lawrence, Larry')
, ('McDonald, Michael')
, ('Nyugen, Nathan')
, ('O''Dell, Oliver')
, ('Peterson, Phillip')
, ('Quigley, Quentin')
, ('Ramallah, Rodolfo')
, ('Smith, Samuel')
, ('Turner, Theodore')
, ('Uno, Umberto')
, ('Victor, Victoria')
, ('Wallace, William')
, ('Xing, Xiopan')
, ('Young, Yvette')
, ('Zapata, Zorro')
, (NULL)
WHILE #Counter2 < 100000
BEGIN
SET #Counter2 += 1
SET #ColA = (SELECT Name FROM #names WHERE ID = ROUND(RAND()*27 +.5,0))
SET #ColB = (SELECT Name FROM #names WHERE ID = ROUND(RAND()*27 +.5,0))
SET #ColC = (SELECT Name FROM #names WHERE ID = ROUND(RAND()*27 +.5,0))
SET #ColD = (SELECT Name FROM #names WHERE ID = ROUND(RAND()*27 +.5,0))
SET #ColE = (SELECT Name FROM #names WHERE ID = ROUND(RAND()*27 +.5,0))
SET #ColF = (SELECT Name FROM #names WHERE ID = ROUND(RAND()*27 +.5,0))
INSERT INTO #transactions2
SELECT #ColA, #ColB, #ColC, #ColD, #ColE, #ColD
END
PRINT CAST(GETDATE() AS DateTime2 (3))
;WITH Dupe
AS (
SELECT *, ROW_NUMBER() OVER
(PARTITION BY ISNULL(ColA,''), ISNULL(ColB,''), ISNULL(ColC,''), ISNULL(ColD,''), ISNULL(ColE,''), ISNULL(ColF,'')
ORDER BY ISNULL(ColA,''), ISNULL(ColB,''), ISNULL(ColC,''), ISNULL(ColD,''), ISNULL(ColE,''), ISNULL(ColF,'')) AS rn
FROM #transactions2
)
SELECT * FROM Dupe WHERE rn > 1 ORDER BY rn
PRINT CAST(GETDATE() AS DateTime2 (3))
Here is a much faster way using a subquery join. It ran in under 10 seconds
select * from transactions x
join (
select Coalesce(ColA, ''),
Coalesce(ColB, ''),
Coalesce(ColC, '')
from transactions
group by Coalesce(ColA, ''),
Coalesce(ColB, ''),
Coalesce(ColC, '')
having count(*) > 1
) dups on
dups.ColA = x.ColA and
dups.ColB = x.ColB and
dups.ColC = x.ColC
The important thing about this query is that it returns both/all rows, not just the duplicate(s)
If this is a one time job, and involves a huge number of rows, and not to be made as a View, then perhaps you'd opt to INSERT SELECT it into a table with UNIQUE index with IGNORE_DUP_KEY option.

How to Calculate Percentage of Revenue based on aggregate column Total sales?

I am trying to add a column that calculate the Percentage of total revenue and I am stuck with the following error:
Error: Msg 207, Level 16, State 1, Line 14 Invalid column name
'Customerkey'.
In that line I’m trying to join Table 1 and Table 3 but MS SQL Server won’t recognize T.Customerkey even though customerkey exists in the dbo.FactInternetSales table.
Also, when I add T.Grand_Tot_Rev in my Group By clause, it returns 0.04 for every row. I know it's wrong because I do not want T.Grand_Tot_Rev to be part of the aggregate, because it should remain constant for every record. How can I achieve that I am looking for? Thank you in advance. By the way, I am using the AdventureWorksDW2012 database.
SELECT fs.CustomerKey ,
M.Total_sales ,
M.Total_cost ,
M.Total_sales - M.Total_cost AS Total_Margin ,
T.Grand_Tot_Rev( M.Total_sales / T.Grand_Tot_Rev ) * 100 AS Prct_Total_Revenue
FROM dbo.FactInternetSales fs , -- Table 1 --
(
SELECT customerkey ,
SUM( SalesAmount )AS Total_Sales ,
SUM( TotalProductCost )Total_cost
FROM dbo.FactInternetSales
GROUP BY customerkey
) M , --Table 2 --
(
SELECT SUM( SalesAmount )AS Grand_Tot_Rev
FROM dbo.FactInternetSales
) T --Table 3 --
WHERE fs.CustomerKey = M.CustomerKey -- Join 1 --
AND M.CustomerKey = T.Customerkey -- Join 2 --
GROUP BY fs.CustomerKey ,
M.Total_sales ,
M.Total_cost ,
T.Grand_Tot_Rev
ORDER BY 2 DESC;
If you want the T.Grand_Tot_Rev as a constant over all rows try removing the second join AND M.CustomerKey = T.Customerkey -- Join 2 -- so the query looks like this:
SELECT fs.CustomerKey ,
M.Total_sales ,
M.Total_cost ,
M.Total_sales - M.Total_cost AS Total_Margin ,
T.Grand_Tot_Rev,
( M.Total_sales / T.Grand_Tot_Rev ) * 100 AS Prct_Total_Revenue
FROM dbo.FactInternetSales fs , -- Table 1 --
(
SELECT customerkey ,
SUM( SalesAmount )AS Total_Sales ,
SUM( TotalProductCost )Total_cost
FROM dbo.FactInternetSales
GROUP BY customerkey
) M , --Table 2 --
(
SELECT SUM( SalesAmount )AS Grand_Tot_Rev
FROM dbo.FactInternetSales
) T --Table 3 --
WHERE fs.CustomerKey = M.CustomerKey -- Join 1 --
--AND M.CustomerKey = T.Customerkey -- Join 2 --
GROUP BY fs.CustomerKey ,
M.Total_sales ,
M.Total_cost ,
T.Grand_Tot_Rev
ORDER BY 2 DESC;
Another way to write the same query that is a bit more compact and might have slightly better performance:
;WITH
T AS (
SELECT SUM(SalesAmount) AS Grand_Tot_Rev
FROM dbo.FactInternetSales
),
M AS (
SELECT customerkey ,
SUM(SalesAmount) AS Total_Sales ,
SUM(TotalProductCost) AS Total_cost
FROM dbo.FactInternetSales
GROUP BY CustomerKey
)
SELECT
customerkey ,
Total_Sales ,
Total_cost,
Total_Sales - Total_cost AS Total_Margin ,
Grand_Tot_Rev,
Total_Sales / Grand_Tot_Rev * 100 AS Prct_Total_Revenue
FROM M, T
ORDER BY 2 DESC;
To see the really small values you can force a conversion to a wider data type:
;WITH
T AS (
SELECT CAST(SUM(SalesAmount) AS decimal) AS Grand_Tot_Rev
FROM dbo.FactInternetSales
),
M AS (
SELECT customerkey ,
CAST(SUM(SalesAmount) AS decimal(15,10)) AS Total_Sales ,
CAST(SUM(TotalProductCost) AS decimal(15,10)) AS Total_cost
FROM dbo.FactInternetSales
GROUP BY CustomerKey
)
SELECT
customerkey ,
Total_Sales ,
Total_cost,
Total_Sales - Total_cost AS Total_Margin ,
Grand_Tot_Rev,
Total_Sales / Grand_Tot_Rev * 100 AS Prct_Total_Revenue
FROM M, T
ORDER BY 2 DESC;

getting distinct rows based on two column values

I am trying to get distinct rows from a temporary table and output them to an aspx page. I am trying to use the value of one column and get the last entry made into that column.
I have been trying to use inner join and max(). However i have been unsuccessful.
Here is the code i have been trying to do it with.
Declare #TempTable table (
viewIcon nvarchar(10),
tenderType nvarchar(20),
diaryIcon int,
customerName nvarchar(100),
projectName nvarchar(100),
diaryEntry nvarchar(max),
diaryDate nvarchar(20),
pid nvarchar(20)
)
insert into #TempTable(
viewIcon,
tenderType,
diaryIcon,
customerName,
projectName,
diaryEntry ,
diaryDate ,
pid
)
select p.viewicon,
p.[Tender Type],
1 diaryicon,
c.[Customer Name],
co.[Last Project],
d.Action,
co.[Diary Date],
p.PID
From Projects2 p Inner Join
(select distinct Pno, max(convert(date,[date of next call],103)) maxdate from ProjectDiary group by Pno
) td on p.PID = td.Pno
Inner Join contacts3 co on co.[Customer Number] = p.[Customer Number]
Inner Join Customers3 c on p.[Customer Number] = c.[Customer Number]
Inner Join ProjectDiary d on td.Pno = d.Pno
Where CONVERT(Date, co.[Diary Date], 103) BETWEEN GETDATE()-120 AND GETDATE()-60
DECLARE #contactsTable TABLE
(pid nvarchar(200),
diaryDate date)
insert into #contactsTable (t.pid, t.diarydate)
select distinct pid as pid, MAX(CONVERT(DATE, diaryDate, 103)) as diaryDate from # TempTable t group by pid
DECLARE #tempContacts TABLE
(pid nvarchar(200))
insert into #tempContacts(pid)
select pid from #contactsTable
DECLARE #tempDiaryDate TABLE (diaryDate date)
insert into #tempDiaryDate(diaryDate)
select distinct MAX(CONVERT(DATE, diaryDate, 103)) from #TempTable
select t.* from #TempTable t inner join (select distinct customerName, M AX(CONVERT(DATE, diaryDate, 103)) AS diaryDate from #TempTable group by customerName) tt on t t.customerName=t.customerName
where t.pid not in
(select Pno from ProjectDiary where convert(date,[Date Of Next Call],103) > GETDATE())
and t.viewIcon <> '098'
and t.viewIcon <> '163'
and t.viewIcon <> '119'
and t.pid in (select distinct pid from #tempContacts)
and CONVERT(DATE, t.diaryDate, 103) in (select distinct CONVERT(DATE, diaryDate, 103) f rom #tempDiaryDate)
order by CONVERT(DATE, tt.diaryDate, 103)
I am trying to get all the distinct customerName's using the max date to determine which record it uses.
Use a subquery. Without going through your entire sql statement, the general idea is:
Select [Stuff]
From table t
Where date = (Select Max(Date) from table
where customer = t.customer)

concatenating single column in TSQL

I am using SSMS 2008 and trying to concatenate one of the rows together based on a different field's grouping. I have two columns, people_id and address_desc. They look like this:
address_desc people_id
---------- ------------
Murfreesboro, TN 37130 F15D1135-9947-4F66-B778-00E43EC44B9E
11 Mohawk Rd., Burlington, MA 01803 C561918F-C2E9-4507-BD7C-00FB688D2D6E
Unknown, UN 00000 C561918F-C2E9-4507-BD7C-00FB688D2D6E Jacksonville, NC 28546 FC7C78CD-8AEA-4C8E-B93D-010BF8E4176D
Memphis, TN 38133 8ED8C601-5D35-4EB7-9217-012905D6E9F1
44 Maverick St., Fitchburg, MA 8ED8C601-5D35-4EB7-9217-012905D6E9F1
Now I want to concatenate the address_desc field / people_id. So the first one here should just display "Murfreesboro, TN 37130" for address_desc. But second person should have just one line instead of two which says "11 Mohawk Rd., Burlington, MA 01803;Unknown, UN 00000" for address_desc.
How do I do this? I tried using CTE, but this was giving me ambiguity error:
WITH CTE ( people_id, address_list, address_desc, length )
AS ( SELECT people_id, CAST( '' AS VARCHAR(8000) ), CAST( '' AS VARCHAR(8000) ), 0
FROM dbo.address_view
GROUP BY people_id
UNION ALL
SELECT p.people_id, CAST( address_list +
CASE WHEN length = 0 THEN '' ELSE ', ' END + c.address_desc AS VARCHAR(8000) ),
CAST( c.address_desc AS VARCHAR(8000)), length + 1
FROM CTE c
INNER JOIN dbo.address_view p
ON c.people_id = p.people_id
WHERE p.address_desc > c.address_desc )
SELECT people_id, address_list
FROM ( SELECT people_id, address_list,
RANK() OVER ( PARTITION BY people_id ORDER BY length DESC )
FROM CTE ) D ( people_id, address_list, rank )
WHERE rank = 1 ;
Here was my initial SQL query:
SELECT a.address_desc, a.people_id
FROM dbo.address_view a
INNER JOIN (SELECT people_id
FROM dbo.address_view
GROUP BY people_id
HAVING COUNT(*) > 1) t
ON a.people_id = t.people_id
order by a.people_id
You can use FOR XML PATH('') like this:
DECLARE #TestData TABLE
(
address_desc NVARCHAR(100) NOT NULL
,people_id UNIQUEIDENTIFIER NOT NULL
);
INSERT #TestData
SELECT 'Murfreesboro, TN 37130', 'F15D1135-9947-4F66-B778-00E43EC44B9E'
UNION ALL
SELECT '11 Mohawk Rd., Burlington, MA 01803', 'C561918F-C2E9-4507-BD7C-00FB688D2D6E'
UNION ALL
SELECT 'Unknown, UN 00000', 'C561918F-C2E9-4507-BD7C-00FB688D2D6E'
UNION ALL
SELECT 'Memphis, TN 38133', '8ED8C601-5D35-4EB7-9217-012905D6E9F1'
UNION ALL
SELECT '44 Maverick St., Fitchburg, MA', '8ED8C601-5D35-4EB7-9217-012905D6E9F1';
SELECT a.people_id,
(SELECT SUBSTRING(
(SELECT ';'+b.address_desc
FROM #TestData b
WHERE a.people_id = b.people_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
,2
,4000)
) GROUP_CONCATENATE
FROM #TestData a
GROUP BY a.people_id
Results:
people_id GROUP_CONCATENATE
------------------------------------ ------------------------------------------------------
F15D1135-9947-4F66-B778-00E43EC44B9E Murfreesboro, TN 37130
C561918F-C2E9-4507-BD7C-00FB688D2D6E 11 Mohawk Rd., Burlington, MA 01803;Unknown, UN 00000
8ED8C601-5D35-4EB7-9217-012905D6E9F1 Memphis, TN 38133;44 Maverick St., Fitchburg, MA