Group By preventing Having clause from giving desired results - sql

This is quite straight-forward but I'm having trouble populating the temp table where the number of distinct values in TypeRec is not greater than one per MatchId and RefNum fields.
CREATE TABLE #TempTable (MatchId int, RefNum char(50), TypeRec char(50))
INSERT INTO #TempTable (MatchId, RefNum, TypeRec)
SELECT t.MatchId,
t.refNum,
t.TypeRec
FROM Transaction t (nolock)
WHERE t.UserText = 'Proposed'
AND t.CompanyId = 4
AND t.RefNum = 'CreditCard'
GROUP BY t.matchId, t.RefNum, t.TypeRec
HAVING count(distinct t.TypeRec) = 1

Just remove typerec from the group by and use min() or max():
INSERT INTO #TempTable (MatchId, RefNum, TypeRec)
SELECT t.MatchId, t.refNum, min(t.TypeRec)
FROM Transaction t (nolock)
WHERE t.UserText = 'Proposed'
AND t.CompanyId = 4
AND t.RefNum = 'CreditCard'
GROUP BY t.matchId, t.RefNum
HAVING count(distinct t.TypeRec) = 1;
This counts the number of TypeRecs for each combination of MatchId and RefNum. It only returns such pairs when there is one TypeRec. The use min() is just to get that value -- the minimum of one value is that value.

Related

How remove from user database type duplicate records with id 221

I have the following stored procedure:
ALTER PROCEDURE [dbo].[sp_ImportSurveys]
#surveys udtSurveys readonly
AS
BEGIN
INSERT INTO Surveys
(Funzione,
Id_Intervento,
Titolo_Intervento,
Titolo_Rilievo,
ImportDownloadDate,
Oggetto_Valutato,
Id_Oggetto_Valutato,
Id,
Id_Banca,
Cod_ABI,
Legal_Entity,
Title,
Descrizione_Rilievo,
Azione_di_Mitigazione,
Owner_Azione_di_Mitigazione,
Utente_Censimento,
Severita_Rilievo,
Data_Scadenza,
Anno,
StatusId)
SELECT Funzione,
Id_Intervento,
Titolo_Intervento,
Titolo_Rilievo,
DataDownload,
Oggetto_Valutato,
Id_Oggetto_Valutato,
CONVERT(nvarchar(450), Id) + Funzione,
Id_Banca,
Cod_ABI,
Legal_Entity,
Titolo_Rilievo,
Descrizione_Rilievo,
Azione_di_Mitigazione,
Owner_Azione_di_Mitigazione,
Utente_Censimento,
Severita_Rilievo,
Data_Scadenza,
Anno,
2
FROM #surveys sur
WHERE NOT EXISTS (Select * from dbo.Surveys WHERE dbo.Surveys.Id = (CONVERT(nvarchar(450), sur.Id) + Funzione))
END
udtSurveys is used like params by stored procedure.
Before inserting records into table surveys I need to remove all duplicate column with Id from udtSurveys.
Would you please show me an example of how use group by or another way to remove duplicated records before inserting to table?
You can simply use a CTE to filter all the duplicate rows from #surveys parameter.
I've updated your query with a cte_tbl by assuming you want to keep the original rows and remove its duplicates.
ALTER PROCEDURE [dbo].[sp_ImportSurveys]
#surveys udtSurveys readonly
AS
BEGIN
;WITH cte_tbl AS (
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Funzione)
FROM #surveys sur
WHERE NOT EXISTS ( SELECT 1
FROM dbo.Surveys
WHERE dbo.Surveys.Id = (CONVERT(nvarchar(450), sur.Id) + Funzione))
)
INSERT INTO Surveys
(Funzione,
Id_Intervento,
Titolo_Intervento,
Titolo_Rilievo,
ImportDownloadDate,
Oggetto_Valutato,
Id_Oggetto_Valutato,
Id,
Id_Banca,
Cod_ABI,
Legal_Entity,
Title,
Descrizione_Rilievo,
Azione_di_Mitigazione,
Owner_Azione_di_Mitigazione,
Utente_Censimento,
Severita_Rilievo,
Data_Scadenza,
Anno,
StatusId)
SELECT Funzione,
Id_Intervento,
Titolo_Intervento,
Titolo_Rilievo,
DataDownload,
Oggetto_Valutato,
Id_Oggetto_Valutato,
CONVERT(nvarchar(450), Id) + Funzione,
Id_Banca,
Cod_ABI,
Legal_Entity,
Titolo_Rilievo,
Descrizione_Rilievo,
Azione_di_Mitigazione,
Owner_Azione_di_Mitigazione,
Utente_Censimento,
Severita_Rilievo,
Data_Scadenza,
Anno,
2
FROM cte_tbl
WHERE RN = 1 -- will only fetch the distinct id-rows
END
One way is to nest the query which gets the duplicate record like. Then inner select gets you the id if there is there are more than 1 record.
declare #id varchar = 'ABC'
delete from [dbo].[TABLE_NAME]
where id in (Select id from [TABLE_NAME]
where [id] = #id
group by id
having count(*) > 1
)

order by Item must appear in the select list if distinct is used

Need to return a temp table in SQL joining another temp table using DISTINCT and ORDER BY clause.
I have a declared a table which returns a few things.
Declare #GrpItems TABLE (ID INT,
Name NVARCHAR(32),
Date DATETIME,
City NVARCHAR(32),
CityCode NVARCHAR(8),
CurrencySort NVARCHAR(16)
)
INSERT INTO #GrpItems
SELECT
ID, Name, Date ,
CityCodeorCaption --this can be two type based on User input CityCode or CityCaption
FROM
RepeatItemTable
Now I have a different table where I want to insert and the procedure returns that table as the final result.
DECLARE #CurrencyTable TABLE (RowNumber INT Identity (1,1),
FK_Currency INT,
Value INT,
CityCode NVARCHAR(16),
CityCaption NVARCHAR(16)
)
INSERT INTO #Currency
SELECT DISTINCT
gb.FK_Currency, cv.Value,
c.CityCode, c.CityCaption
FROM
Balance b
JOIN
Currency c ON c.PK_Currency = b.FK_Currency
JOIN
#GrpItems gi ON c.FK_Grpitem = gi.PK_Grpitem
ORDER BY
gi.CityCodeorName
I know somewhere I need group by but I am not sure or a select clause in where filter
I think
ORDER BY
gi.CityCodeOrNAME
WHEN 'City' THEN City
ELSE CityCode ASC
END
Which does not seem to work? I need the Distinct because it might break some other logic.
Select * from #CurrencyTable
You can always use group by instead of select distinct. That will solve your problem:
SELECT gb.FK_Currency, cv.Value, c.CityCode, c.CityCaption
FROM Balance b JOIN
Currency c
ON c.PK_Currency = b.FK_Currency JOIN
#GrpItems gi
ON c.FK_Grpitem = gi.PK_Grpitem
GROUP BY gb.FK_Currency, cv.Value, c.CityCode, c.CityCaption
ORDER BY MAX(gi.CityCodeorName) ;
Note the use of the aggregation function in the ORDER BY.
ORDER BY CASE WHEN CityCodeOrNAME = 'City'
THEN City
ELSE CityCode
END
In case you need differnt orders you can also separate them
ORDER BY CASE WHEN CityCodeOrNAME = 'City' THEN City END DESC,
CASE WHEN CityCodeOrNAME <> 'City' THEN CityCode END ASC

How to update specific values in a column with other values from same column in the same table? - SQL Server

So lets say I have a Table "Stuff".
It has 3 columns.
So Job Code is supposed to be the same for both manager and the employee under that manager(like it is where jobcode= 000 ). That is the normal scenario.
However in some cases, The Manager will have "ABC" as jobcode.
In those cases, I need to replace "ABC" with the jobcode value of the most recent employee under that manager.
For example, for Manager A1, I need to replace his jobcode of ABC with 234,considering B1 is the most recent employee under him.
For manager A, his jobcode of ABC will be replaced with 121 since B is the only employee under him.
I wrote this query but it doesn't seem to work.
Update X
Set X.JobCode=Y.JobCode
FROM STUFF X
INNER JOIN STUFF Y
ON X.MGRCODE=Y.MGRCODE
AND X.JOBCODE = 'ABC"
AND Y.JOBCODE = ( SELECT TOP 1 JOBCODE FROM STUFF WHERE EMPCODE<>Y.MGRCODE AND
Y.MGRCODE IN (SELECT MGRCODE FROM STUFF WHERE EMPCODE=MGRCODE AND JOBCODE='ABC')
;WITH cteJobCodeRowNum AS (
SELECT
ManagerCode
,JobCode
,ROW_NUMBER() OVER (PARTITION BY ManagerCode ORDER BY UpdateDate DESC) as RowNumber
FROM
#Table
)
UPDATE t
SET JobCode = r.JobCode
FROM
#Table t
INNER JOIN cteJobCodeRowNum r
ON t.EmpCode = r.ManagerCode
AND r.RowNumber = 1
AND t.JobCode <> r.JobCode
WHERE
t.JobCode = 'ABC'
Use a partitioned window function to generate a Row Number to choose the Job Code you want. Partition by ManagerCode and order by UpdateDate descending. Then join that common table expression (or derived table if you nest it) to your table based on EmpCode = ManagerCode To Update The Managers record. You can also then constrain it only to when the Manager has a Job Code of 'ABC' and the job code returned by the row number is different so you only update a specific set of rows.
Another similar method is to create your own row number by using a related cross apply such as:
UPDATE t1
SET JobCode = NewJobCode
FROM
#Table t1
CROSS APPLY (SELECT TOP 1 JobCode as NewJobCode FROM #Table t2 WHERE t1.EmpCode = t2.ManagerCode ORDER BY t2.UpdateDate DESC) n
WHERE
t1.JobCode = 'ABC'
AND t1.JobCode <> n.NewJobCode
Here is a full working example of the window function method:
DECLARE #Table AS TABLE (JobCode CHAR(3), EmpCode VARCHAR(2), ManagerCode VARCHAR(2), UpdateDate DATETIME)
INSERT INTO #Table (JobCode, EmpCode, ManagerCode, UpdateDate)
VALUES ('ABC','A','A',GETDATE()-1),('121','B','B',GETDATE()-1)
,('ABC','A1','A1',GETDATE()-1)
,('234','B1','A1',GETDATE()+1)
,('342','C1','A1',GETDATE()-1)
,('000','A2','A2',GETDATE()-1)
,('000','B2','B2',GETDATE()-1)
SELECT *
FROM
#Table
;WITH cteJobCodeRowNum AS (
SELECT
ManagerCode
,JobCode
,ROW_NUMBER() OVER (PARTITION BY ManagerCode ORDER BY UpdateDate DESC) as RowNumber
FROM
#Table
)
UPDATE t
SET JobCode = r.JobCode
FROM
#Table t
INNER JOIN cteJobCodeRowNum r
ON t.EmpCode = r.ManagerCode
AND r.RowNumber = 1
AND t.JobCode <> r.JobCode
WHERE
t.JobCode = 'ABC'
SELECT *
FROM
#Table

MSSQL: How to get multiple values from another table

For a given "CustomerId" I need to get 4 related values from a column ("CompanySales") in another table.
I have joined the two tables and, with the query below, manage to get 2 "CompanySales" values from the column in the other table.
How do I do this do get 4 values (I need CompanySales for "WeekNumber" = 1,2,3 and 4)
This is the SQL query I have to secure "CompanySales" for "Weeknumber" = 1 and 2:
Declare #TempTable1 table
(
CustomerID INT,
CustomerName Varchar (50),
CompanySales DEC (8,2),
WeekNumber INT
)
INSERT INTO #TempTable1 ("CustomerID","CustomerName", "WeekNumber")
SELECT Customer.CustomerID, Customer.CustomerName, Company.WeekNumber, company.Sales
FROM Customer INNER JOIN
Company ON Customer.CustomerID = Company.CustomerID;
With tblDifference as
(
Select Row_Number() OVER (Order by WeekNumber) as RowNumber,CustomerID,CustomerName, companysales, WeekNumber from #TempTable1
)
Select Top (50) cur.CustomerID, Cur.CustomerName, Cur.WeekNumber as CurrentWeek, Prv.WeekNumber as PreviousWeek, Cur.CompanySales as CurrentSales, Prv.CompanySales as PreviousSales, CAST(((Cur.CompanySales-Prv.CompanySales)/Prv.CompanySales)*100 As Decimal(8,2)) as PercentChange from
tblDifference Cur Left Outer Join tblDifference Prv
On Cur.CustomerID=Prv.CustomerID
Where cur.WeekNumber = 1 AND prv.WeekNumber = 2
Order BY PercentChange ASC
How about adding between there cur.WeekNumber Between 1 and 4?

SUM by two different GROUP BY

I'm getting the wrong result from my report. Maybe i'm missing something simple.
The report is an inline table-valued-function that should count goods movement in our shop and how often these spareparts are claimed(replaced in a repair).
The problem: different spareparts in the shop-table(lets call it SP) can be linked to the same sparepart in the "repair-table"(TSP). I need the goods movement of every sparepart in SP and the claim-count of every distinct sparepart in TSP.
This is a very simplified excerpt of the relevant part:
create table #tsp(id int, name varchar(20),claimed int);
create table #sp(id int, name varchar(20),fiTsp int,ordered int);
insert into #tsp values(1,'1235-6044',300);
insert into #tsp values(2,'1234-5678',400);
insert into #sp values(1,'1235-6044',1,30);
insert into #sp values(2,'1235-6044',1,40);
insert into #sp values(3,'1235-6044',1,50);
insert into #sp values(4,'1234-5678',2,60);
WITH cte AS(
select tsp.id As TspID,tsp.name as TspName,tsp.claimed As Claimed
,sp.id As SpID,sp.name As SpName,sp.ordered As Ordered
from #sp sp inner join #tsp tsp
on sp.fiTsp=tsp.id
)
SELECT TspName, SUM(Claimed) As Claimed, Sum(Ordered) As Ordered
FROM cte
Group By TspName
drop table #tsp;
drop table #sp;
Result:
TspName Claimed Ordered
1234-5678 400 60
1235-6044 900 120
The Ordered-count is correct but the Claimed-count should be 300 instead of 900 for TspName='1235-6044'.
I need to group by Tsp.ID for the claim-count and group by Sp.ID for the order-count. But how in one query?
Edit: Actually the TVF looks like(note that getOrdered and getClaimed are SVFs and that i'm grouping in the outer select on TSP's Category):
CREATE FUNCTION [Gambio].[rptReusedStatistics](
#fromDate datetime
,#toDate datetime
,#fromInvoiceDate datetime
,#toInvoiceDate datetime
,#idClaimStatus varchar(50)
,#idSparePartCategories varchar(1000)
,#idSpareParts varchar(1000)
)
RETURNS TABLE AS
RETURN(
WITH ExclusionCat AS(
SELECT idSparePartCategory AS ID From tabSparePartCategory
WHERE idSparePartCategory IN(- 3, - 1, 6, 172,168)
), Report AS(
SELECT Cat.SparePartCategoryName AS Category
,TSP.SparePartDescription AS Part
,TSP.SparePartName AS PartNumber
,SP.Inventory
,Gambio.getGoodsIn(SP.idSparePart,#FromDate,#ToDate) GoodsIn
,Gambio.getOrdered(SP.idSparePart,#FromDate,#ToDate) Ordered
--,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
-- Gambio.getClaimed(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus,NULL)END AS Claimed
,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
Gambio.getClaimed(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus,1)END AS ClaimedReused
,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
Gambio.getCostSaving(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus)END AS Costsaving
FROM Gambio.SparePart AS SP
INNER JOIN tabSparePart AS TSP ON SP.fiTabSparePart = TSP.idSparePart
INNER JOIN tabSparePartCategory AS Cat
ON Cat.idSparePartCategory=TSP.fiSparePartCategory
WHERE Cat.idSparePartCategory NOT IN(SELECT ID FROM ExclusionCat)
AND (#idSparePartCategories IS NULL
OR TSP.fiSparePartCategory IN(
SELECT Item From dbo.Split(#idSparePartCategories,',')
)
)
AND (#idSpareParts IS NULL
OR TSP.idSparePart IN(
SELECT Item From dbo.Split(#idSpareParts,',')
)
)
)
SELECT Category
--, Part
--, PartNumber
, SUM(Inventory)As InventoryCount
, SUM(GoodsIn) As GoodsIn
, SUM(Ordered) As Ordered
--, SUM(Claimed) As Claimed
, SUM(ClaimedReused)AS ClaimedReused
, SUM(Costsaving) As Costsaving
, Count(*) AS PartCount
FROM Report
GROUP BY Category
)
Solution:
Thanks to Aliostad i've solved it by first grouping and then joining(actual TVF, reduced to a minimum):
WITH Report AS(
SELECT Cat.SparePartCategoryName AS Category
,TSP.SparePartDescription AS Part
,TSP.SparePartName AS PartNumber
,SP.Inventory
,SP.GoodsIn
,SP.Ordered
,Gambio.getClaimed(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus,1) AS ClaimedReused
,Gambio.getCostSaving(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus) AS Costsaving
FROM (
SELECT GSP.fiTabSparePart
,SUM(GSP.Inventory)AS Inventory
,SUM(Gambio.getGoodsIn(GSP.idSparePart,#FromDate,#ToDate))AS GoodsIn
,SUM(Gambio.getOrdered(GSP.idSparePart,#FromDate,#ToDate))AS Ordered
FROM Gambio.SparePart GSP
GROUP BY GSP.fiTabSparePart
)As SP
INNER JOIN tabSparePart TSP ON SP.fiTabSparePart = TSP.idSparePart
INNER JOIN tabSparePartCategory AS Cat
ON Cat.idSparePartCategory=TSP.fiSparePartCategory
)
SELECT Category
, SUM(Inventory)As InventoryCount
, SUM(GoodsIn) As GoodsIn
, SUM(Ordered) As Ordered
, SUM(ClaimedReused)AS ClaimedReused
, SUM(Costsaving) As Costsaving
, Count(*) AS PartCount
FROM Report
GROUP BY Category
You are JOINing first and then GROUPing by. You need to reverse it, GROUP BY first and then JOIN.
So here in my subquery, I group by first and then join:
select
claimed,
ordered
from
#tsp
inner JOIN
(select
fitsp,
SUM(ordered) as ordered
from
#sp
group by
fitsp) as SUMS
on
SUMS.fiTsp = id;
I think you just need to select Claimed and add it to the Group By in order to get what you are looking for.
WITH cte AS(
select tsp.id As TspID,tsp.name as TspName,tsp.claimed As Claimed
,sp.id As SpID,sp.name As SpName,sp.ordered As Ordered
from #sp sp inner join #tsp tsp
on sp.fiTsp=tsp.id )
SELECT TspName, Claimed, Sum(Ordered) As Ordered
FROM cte
Group By TspName, Claimed
Your cte is an inner join between tsp and sp, which means that the data you're querying looks like this:
SpID Ordered TspID TspName Claimed
1 30 1 1235-6044 300
2 40 1 1235-6044 300
3 50 1 1235-6044 300
4 60 2 1234-5678 400
Notice how TspID, TspName and Claimed all get repeated. Grouping by TspName means that the data gets grouped in two groups, one for 1235-6044 and one for 1234-5678. The first group has 3 rows on which to run the aggregate functions, the second group only one. That's why your sum(Claimed) will get you 300*3=900.
As Aliostad suggested, you should first group by TspID and do the sum of Ordered and then join to tsp.
No need to join, just subselect:
create table #tsp(id int, name varchar(20),claimed int);
create table #sp(id int, name varchar(20),fiTsp int,ordered int);
insert into #tsp values(1,'1235-6044',300);
insert into #tsp values(2,'1234-5678',400);
insert into #sp values(1,'1235-6044',1,30);
insert into #sp values(2,'1235-6044',1,40);
insert into #sp values(3,'1235-6044',1,50);
insert into #sp values(4,'1234-5678',2,60);
WITH cte AS(
select tsp.id As TspID,tsp.name as TspName,tsp.claimed As Claimed
,sp.id As SpID,sp.name As SpName,sp.ordered As Ordered
from #sp sp inner join #tsp tsp
on sp.fiTsp=tsp.id
)
SELECT id, name, SUM(claimed) as Claimed, (SELECT SUM(ordered) FROM #sp WHERE #sp.fiTsp = #tsp.id GROUP BY #sp.fiTsp) AS Ordered
FROM #tsp
GROUP BY id, name
drop table #tsp;
drop table #sp;
Produces:
id name Claimed Ordered
1 1235-6044 300 120
2 1234-5678 400 60
-- EDIT --
Based on the additional info, this is how I might try to split the CTE to form the data as per the example. I fully admit that Aliostad's approach may yield a cleaner query but here's an attempt (completely blind) using the subselect:
CREATE FUNCTION [Gambio].[rptReusedStatistics](
#fromDate datetime
,#toDate datetime
,#fromInvoiceDate datetime
,#toInvoiceDate datetime
,#idClaimStatus varchar(50)
,#idSparePartCategories varchar(1000)
,#idSpareParts varchar(1000)
)
RETURNS TABLE AS
RETURN(
WITH ExclusionCat AS (
SELECT idSparePartCategory AS ID From tabSparePartCategory
WHERE idSparePartCategory IN(- 3, - 1, 6, 172,168)
), ReportSP AS (
SELECT fiTabSparePart
,Inventory
,Gambio.getGoodsIn(idSparePart,#FromDate,#ToDate) GoodsIn
,Gambio.getOrdered(idSparePart,#FromDate,#ToDate) Ordered
FROM Gambio.SparePart
), ReportTSP AS (
SELECT TSP.idSparePart
,Cat.SparePartCategoryName AS Category
,TSP.SparePartDescription AS Part
,TSP.SparePartName AS PartNumber
,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
Gambio.getClaimed(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus,1)END AS ClaimedReused
,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
Gambio.getCostSaving(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus)END AS Costsaving
FROM tabSparePart AS TSP
INNER JOIN tabSparePartCategory AS Cat
ON Cat.idSparePartCategory=TSP.fiSparePartCategory
WHERE Cat.idSparePartCategory NOT IN(SELECT ID FROM ExclusionCat)
AND (#idSparePartCategories IS NULL
OR TSP.fiSparePartCategory IN(
SELECT Item From dbo.Split(#idSparePartCategories,',')
)
)
AND (#idSpareParts IS NULL
OR TSP.idSparePart IN(
SELECT Item From dbo.Split(#idSpareParts,',')
)
)
)
SELECT Category
--, Part
--, PartNumber
, (SELECT SUM(Inventory) FROM ReportSP WHERE ReportSP.fiTabSparePart = idSparePart GROUP BY fiTabSparePart) AS Inventory
, (SELECT SUM(GoodsIn) FROM ReportSP WHERE ReportSP.fiTabSparePart = idSparePart GROUP BY fiTabSparePart) AS GoodsIn
, (SELECT SUM(Ordered) FROM ReportSP WHERE ReportSP.fiTabSparePart = idSparePart GROUP BY fiTabSparePart) AS Ordered
, Claimed
, ClaimedReused
, Costsaving
, Count(*) AS PartCount
FROM ReportTSP
GROUP BY Category
)
Without a better understanding of the whole schema it's difficult to cover for all the eventualities but whether this works or not (I suspect PartCount will be 1 for all instances) hopefully it'll give you some fresh thoughts for alternate approaches.
SELECT
tsp.name
,max(tsp.claimed) as claimed
,sum(sp.ordered) as ordered
from #sp sp
inner join #tsp tsp
on sp.fiTsp=tsp.id
GROUP BY tsp.name