If I have several customers responding month after month, I want to count them only in the first month they responded. I can achieve this by creating temp tables for each month and comparing month over month, but it looks ugly with several temp tables. I'm pretty sure that there's a better way to do this (I don't know if Rank() will work). Can someone show me code please?
declare #Something table
(
CustID Char(10),
MthId char(2),
ResponseDate datetime
)
insert #Something
select 'Cust1', '1', '5/6/13' union all
select 'Cust1', '2', '6/13/13' union all
select 'Cust1', '3', '7/13/13' union all
select 'Cust2', '1', '5/20/13' union all
select 'Cust2', '2', '6/22/13' union all
select 'Cust3', '2', '6/20/13' union all
select 'Cust4', '2', '6/24/13' union all
select 'Cust4', '3', '7/24/13' union all
select 'Cust5', '4', '8/28/13' union all
select 'Cust6', '3', '7/24/13'
This is the output I'm expecting in 3 columns (I don't really need the 2nd col - it's there to explain further)
Month, How many "cumulative" new customers responded that month, What's the cumulative percent of total customers contacted new every month.
MthId NewCustomerResponse CumulativeNewCustomerResponse Cumulative%Responded
1 2 2 33.3
2 2 4 66.7
3 1 5 83.3
4 1 6 100.0
Including your new column:
SQLFiddle to the solution
;with cte as(
select
ROW_NUMBER() over (partition by CustID order by responseDate) as seq
,* from #Something
)
,cte2 as(
select
MthId
,(select Count(*) from cte t2 where t1.MthId=t2.MthId and t2.seq=1) NewCustomerResponse
,(select COUNT(*) from cte t2 where t1.MthId>=t2.MthId and t2.seq=1) CumulativeNewCustomerResponse
,(select COUNT(CustID) from cte where seq=1) total
from cte t1
group by MthId
)
select
MthID
,NewCustomerResponse
,CumulativeNewCustomerResponse
,(cast(CumulativeNewCustomerResponse as decimal(3,1))/CAST(total as decimal(3,1)))*100
from cte2 t1
check this,
declare #Something table
(
CustID Char(10),
MthId char(2),
ResponseDate datetime
)
insert into #Something
select 'Cust1', '1', '5/6/13'
union all
select 'Cust1', '2', '6/13/13'
union all
select 'Cust1', '3', '7/13/13'
union all
select 'Cust2', '1', '5/20/13'
union all
select 'Cust2', '2', '6/22/13'
union all
select 'Cust3', '2', '6/20/13' union all
select 'Cust4', '2', '6/24/13' union all
select 'Cust4', '3', '7/24/13' union all
select 'Cust5', '4', '8/28/13' union all
select 'Cust6', '3', '7/24/13'
;with CTE as
(select *,dense_rank()over(partition by custid order by mthid)rn from #Something)
,CTE1 as
(select a.MthId,count(*) NewCustomerResponse from cte a where rn=1 group by a.MthId )
,cte2 as
(select sum(NewCustomerResponse) totalresponse from cte1)
,cte4 as
(
select a.MthId
,(Select sum(NewCustomerResponse) from CTE1 c where c.mthid<=a.mthid) CumulativeNewCustomerResponse
from cte1 a cross apply cte2 b
)
select a.MthId,a.NewCustomerResponse
,(Select sum(NewCustomerResponse)from CTE1 c where c.mthid<=a.mthid)CumulativeNewCustomerResponse
,case when b.totalresponse>0 then cast((d.CumulativeNewCustomerResponse /cast(b.totalresponse as float))*100 as decimal(10,2)) else 0 end [Cumulative%Responded]
from cte1 a
inner join cte4 d on a.MthId=d.MthId
cross apply cte2 b
Related
I am trying to get a result set of Top Customers which is ordered by a rank value based on year and total. Easy, right?
BUT, now I want to specify a year, and have ALL rows for that customer return the rank value for that year.
For example, say I have the following data (rank hardcoded for ease):
SELECT * FROM
(
SELECT 'Customer A' as Cust,'123.45' as Total,'2016' as [year],1 as [rank]
UNION
SELECT 'Customer A','123.45','2017',3
UNION
SELECT 'Customer B','46.67','2016',2
UNION
SELECT 'Customer B','423.45','2017',1
UNION
SELECT 'Customer B','123.45','2018',1
UNION
SELECT 'Customer C','23.45','2016',3
UNION
SELECT 'Customer C','223.45','2017',2
UNION
SELECT 'Customer C','23.45','2018',2
) as a
ORDER BY a.[year], a.[rank]
If I specify year 2016, I want to pick the rank value for the year 2016 for each customer, and return that for the customer for each of that customer's rows in the result set - should look like the following:
=>
The closest I can manage is the following, but it just NULLs the other cells:
DECLARE #RankBy VARCHAR(4) = '2016'
SELECT [Year],
Cust,
[Total],
[rank] = (SELECT a.[rank] WHERE Cust = a.Cust AND [Year] = #RankBy)
FROM
(
SELECT 'Customer A' as Cust,'123.45' as Total,'2016' as [year],1 as [rank]
UNION
SELECT 'Customer A','123.45','2017',3
UNION
SELECT 'Customer B','46.67','2016',2
UNION
SELECT 'Customer B','423.45','2017',1
UNION
SELECT 'Customer B','123.45','2018',1
UNION
SELECT 'Customer C','23.45','2016',3
UNION
SELECT 'Customer C','223.45','2017',2
UNION
SELECT 'Customer C','23.45','2018',2
) as a
ORDER BY a.Cust, a.rank
I know I can do this with a temp table and an update statement, but I'm trying to find a way to do it in a single select statement if possible.
(In case anyone is wondering, this is for an SSRS Report, but I don't see that being relevant here.)
Try a Windowed Aggregate over a CASE:
MIN(CASE WHEN [Year] = #RankBy THEN a.[rank] END)
OVER (PARTITION BY Cust)
I want to get the top 5 Zipcodes for each Store with the highest Customers in them (zipcodes).
Please find below my query:
SELECT T.[Store], T.[ZipCode], Count(T.[Customer])
FROM ( SELECT T.[Store], T.[ZipCode],
Count(T.[Customer]) row_number() over (Partition By T.[StoreGitanjali] Order By Count (T.[Customer]) desc) as RN
FROM [Marketing].[dbo].[Poscus] as T
Group By T.[StoreGitanjali], T.[ZipCode]) as T
where T.RN <=5
Group By T.[StoreGitanjali], T.[ZipCode]
Please let me know how to use Count here in this scenario.
Thank you!
Example
CREATE TABLE #t
(
ID INT IDENTITY(1,1),
Customer NVARCHAR(3),
Store NVARCHAR(5),
ZIP INT
)
INSERT INTO #t VALUES('a', 'XYZ', 1234)
,('b', 'XYZ', 1234)
,('c', 'PQR', 1231)
,('d', 'PQR', 1231)
,('e', 'PQR', 1231)
,('f', 'XYZ', 1232)
,('g', 'XYZ', 1232)
,('h', 'XYZ', 1232)
,('i', 'PQR', 1236)
,('j', 'PQR', 1236)
,('k', 'LMN', 1237)
SELECT * FROM #t
The solution is, Set WHERE part < 2 according to your requirement.
SELECT TotalCustomer, Store, ZIP, Part FROM (
SELECT
COUNT(1) AS TotalCustomer,
Store,
ZIP,
ROW_NUMBER() OVER (PARTITION BY Store ORDER BY Store) AS Part
FROM #t
GROUP BY Store, ZIP
) t
WHERE Part < 2
ORDER BY Part
;WITH CTE
AS(
SELECT Store
,Zip
,COUNT(DISTINCT Customer) AS CustCount
FROM #t
GROUP BY Store,Zip
--ORDER BY Store,Zip
)
SELECT A.*
FROM(
SELECT *
--,DENSE_RANK() OVER(PARTITION BY Store ORDER BY CustCount DESC) AS DenRank
,ROW_NUMBER() OVER(PARTITION BY Store ORDER BY CustCount DESC) AS DenRank
FROM CTE
--ORDER BY Store,Zip
) AS A
WHERE A.DenRank <= 2
I Would like the first date of each group to repeat for the rest of the rows withing each group
You could use window expressions and grouping;
FIRST_VALUE (Transact-SQL)
You would need to partition by your first column. to get the split of A and B.
For example;
with cteTempData
(
[Code]
, [Date]
)
as
(
select 'A',cast('2015-9-4' as date)
union all select 'A','2015-9-4'
union all select 'A','2015-9-4'
union all select 'A','2015-9-16'
union all select 'B','2015-9-16'
union all select 'B','2015-9-22'
union all select 'B','2015-9-22'
union all select 'B','2015-10-26'
union all select 'B','2015-10-30'
)
select
[Code]
, [Date]
, FIRST_VALUE([Date]) over (partition by [Code] order by [Date]) as [First_Date]
from cteTempData
Using the first_value syntax also allows you to work with other columns in that ordered record....
with cteTempData
(
[Code]
, [Date]
, [Comment]
)
as
(
select 'A',cast('2015-9-4' as date),'One'
union all select 'A','2015-9-4','Two'
union all select 'A','2015-9-4','Three'
union all select 'A','2015-9-16','Four'
union all select 'B','2015-9-16','Five'
union all select 'B','2015-9-22','Six'
union all select 'B','2015-9-22','Seven'
union all select 'B','2015-10-26','Eight'
union all select 'B','2015-10-30','Nine'
)
select
[Code]
, [Date]
, FIRST_VALUE([Date]) over (partition by [Code] order by [Date]) as [First_Date]
, FIRST_VALUE([Comment]) over (partition by [Code] order by [Date]) as [First_Comment]
from cteTempData
Use MIN() Over ()
Declare #Table table (Grp varchar(25),Date date)
Insert into #Table values
('A','2015-09-04'),
('A','2015-09-05'),
('A','2015-09-10'),
('B','2015-10-04'),
('B','2015-10-05'),
('B','2015-10-10')
Select *
,GrpDate = min(Date) over (Partition By Grp)
From #Table
Returns
Grp Date GrpDate
A 2015-09-04 2015-09-04
A 2015-09-05 2015-09-04
A 2015-09-10 2015-09-04
B 2015-10-04 2015-10-04
B 2015-10-05 2015-10-04
B 2015-10-10 2015-10-04
You could use MIN with the OVER-clause
SELECT t.ColumnA,
DateCol = MIN( t.DateCol ) OVER ( PARTITION BY t.ColumnA ),
OtherColumns
FROM dbo.TableName t
you can go with a CROSS JOIN or FIRST_VALUE.
Declare #Yourtable table (groupCol varchar(25),firstDate date)
Insert into #Yourtable values
('A','2015-09-04'),
('A','2015-09-05'),
('A','2015-09-10'),
('B','2015-10-04'),
('B','2015-10-05'),
('B','2015-10-10')
SELECT a.*,b.firstDate
FROM #Yourtable a
CROSS JOIN (SELECT groupCol,MIN(firstDate) firstDate
FROM #Yourtable b
GROUP BY groupCol)b
WHERE a.groupCol =b.groupCol
OR
SELECT a.*,FIRST_VALUE(a.firstDate) OVER (PARTITION BY groupCol ORDER BY groupCol ASC) AS firstDate
FROM #Yourtable a
I am using SQL Server 2008. I have data by each employee for each day. Below is the sample data.
WITH RawData as
(
SELECT '10001' AS EmpNo,'2015-01-01' as AttendanceDate,'FS' AS ShiftCode UNION
SELECT '10001','2015-01-02','WO' UNION
SELECT '10001','2015-01-03','FS' UNION
SELECT '10001','2015-01-04','FS' UNION
SELECT '10001','2015-01-05','FS' UNION
SELECT '10001','2015-01-06','FS' UNION
SELECT '10001','2015-01-07','FS' UNION
SELECT '10001','2015-01-08','FS' UNION
SELECT '10001','2015-01-09','WO' UNION
SELECT '10001','2015-01-10','FS' UNION
SELECT '10001','2015-01-11','FS' UNION
SELECT '10001','2015-01-12','FS' UNION
SELECT '10001','2015-01-13','FS' UNION
SELECT '10001','2015-01-14','FS' UNION
SELECT '10001','2015-01-15','FS' UNION
SELECT '10001','2015-01-16','WO' UNION
SELECT '10001','2015-01-17','FS' UNION
SELECT '10001','2015-01-18','FS' UNION
SELECT '10001','2015-01-19','FS' UNION
SELECT '10001','2015-01-20','FS' UNION
SELECT '10001','2015-01-21','FS' UNION
SELECT '10001','2015-01-22','FS' UNION
SELECT '10001','2015-01-23','WO' UNION
SELECT '10001','2015-01-24','FS' UNION
SELECT '10001','2015-01-25','FS' UNION
SELECT '10001','2015-01-26','FS' UNION
SELECT '10001','2015-01-27','FS' UNION
SELECT '10001','2015-01-28','FS' UNION
SELECT '10001','2015-01-29','FS' UNION
SELECT '10001','2015-01-30','WO' UNION
SELECT '10001','2015-01-31','FS' UNION
SELECT '10002','2015-01-01','FS' UNION
SELECT '10002','2015-01-02','WO' UNION
SELECT '10002','2015-01-03','WO' UNION
SELECT '10002','2015-01-04','FS' UNION
SELECT '10002','2015-01-05','FS' UNION
SELECT '10002','2015-01-06','FS' UNION
SELECT '10002','2015-01-07','FS' UNION
SELECT '10002','2015-01-08','FS' UNION
SELECT '10002','2015-01-09','WO' UNION
SELECT '10002','2015-01-10','WO' UNION
SELECT '10002','2015-01-11','FS' UNION
SELECT '10002','2015-01-12','FS' UNION
SELECT '10002','2015-01-13','FS' UNION
SELECT '10002','2015-01-14','FS' UNION
SELECT '10002','2015-01-15','FS' UNION
SELECT '10002','2015-01-16','WO' UNION
SELECT '10002','2015-01-17','WO' UNION
SELECT '10002','2015-01-18','FS' UNION
SELECT '10002','2015-01-19','FS' UNION
SELECT '10002','2015-01-20','FS' UNION
SELECT '10002','2015-01-21','FS' UNION
SELECT '10002','2015-01-22','FS' UNION
SELECT '10002','2015-01-23','WO' UNION
SELECT '10002','2015-01-24','WO' UNION
SELECT '10002','2015-01-25','FS' UNION
SELECT '10002','2015-01-26','FS' UNION
SELECT '10002','2015-01-27','FS' UNION
SELECT '10002','2015-01-28','FS' UNION
SELECT '10002','2015-01-29','FS' UNION
SELECT '10002','2015-01-30','WO' UNION
SELECT '10002','2015-01-31','WO')
SELECT * FROM RawData Order By EmpNo,AttendanceDate
How to write SQL Query to get following output based on this sample data ? The workweek of each employee starts on a Day after weekly off and it can be any day (mon, tue etc). The shift code denotes WO: weekly off, FS: First Shift, SS: Second Shift.
EmpNo WeekFrom WeekTo
10001 2015-01-01 2015-01-02
10001 2015-01-03 2015-01-09
10001 2015-01-10 2015-01-16
10001 2015-01-17 2015-01-23
10001 2015-01-24 2015-01-30
10001 2015-01-31 2015-01-31
10002 2015-01-01 2015-01-03
10002 2015-01-04 2015-01-10
10002 2015-01-11 2015-01-17
10002 2015-01-18 2015-01-24
10002 2015-01-25 2015-01-31
Got a solution. But its taking quite a long time on live table with 1 Million rows. Have I done something wrong in a query ? Or there is a better way of doing this.
WITH RawData as
(
-- Insert above data here.
)
,ProcessData AS (
SELECT EmpNo,AttendanceDate,ShiftCode,RowID = ROW_NUMBER() OVER (
ORDER BY EmpNo, AttendanceDate
), WeekNo = 1 FROM RawData
)
,FinalData
AS (
SELECT EmpNo, AttendanceDate, ShiftCode, RowID, WeekNo = 1
FROM ProcessData DA
WHERE RowID = 1
UNION ALL
SELECT DA.EmpNo, DA.AttendanceDate, DA.ShiftCode, DA.RowID,
WeekNo = (CASE WHEN FinalData.EmpNo != DA.EmpNo THEN 1 ELSE FinalData.WeekNo + (CASE WHEN (FinalData.ShiftCode = 'WO' AND DA.ShiftCode != 'WO') THEN 1 ELSE 0 END) END)
FROM FinalData
INNER JOIN ProcessData DA ON DA.RowID = FinalData.RowID + 1
)
SELECT EmpNo, MIN(AttendanceDate) AS StartDate, MAX(AttendanceDate) AS EndDate, WeekNo
FROM FinalData
GROUP BY EmpNo, WeekNo
ORDER BY EmpNo, WeekNo
Try this:
SQL Fiddle
;WITH RawData AS (
-- Your insert statements here
),
Cte AS(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY EmpNo, grp ORDER BY AttendanceDate DESC)
FROM (
SELECT *,
grp = DATEADD(DAY, -ROW_NUMBER() OVER(PARTITION BY EmpNo ORDER BY AttendanceDate), AttendanceDate)
FROM RawData
WHERE ShiftCode = 'WO'
)t
),
CteWeekOff AS(
SELECT EmpNo, AttendanceDate, ShiftCode FROM cte WHERE RN = 1
),
CteFinal AS(
SELECT
EmpNo,
WeekFrom = MIN(AttendanceDate),
Weekto = MAX(AttendanceDate)
FROM (
SELECT *,
grp = DATEADD(DAY, - ROW_NUMBER() OVER(PARTITION BY EmpNo ORDER BY AttendanceDate), AttendanceDate)
FROM RawData
WHERE ShiftCode <> 'WO'
)t
GROUP BY EmpNo, grp
)
SELECT
EmpNo,
WeekFrom = x.WeekFrom,
WeekTo = w.AttendanceDate
FROM CteWeekOff w
CROSS APPLY(
SELECT TOP 1 WeekFrom
FROM CteFinal r
WHERE
r.EmpNo = w.EmpNo
AND r.WeekFrom <= w.AttendanceDate
ORDER BY r.WeekFrom DESC
)x(WeekFrom)
UNION ALL
SELECT
EmpNo,
WeekFrom = x.WeekFrom,
WeekTo = t.AttendanceDate
FROM (
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY EmpNo ORDER BY AttendanceDate DESC)
FROM RawData
)t
CROSS APPLY(
SELECT TOP 1 AttendanceDate
FROM CteFinal r
WHERE
r.EmpNo = t.EmpNo
AND r.WeekFrom < t.AttendanceDate
ORDER BY r.WeekFrom DESC
)x(WeekFrom)
WHERE
RN = 1
AND ShiftCode <> 'WO'
ORDER BY EmpNo, WeekFrom
Finally this worked. 5 seconds on 230,000 records. I will go ahead with my solution. Thanks for your time. Hope this solution helps someone.
-- Step 1 : Save it to temp table
SELECT EmpNo,AttendanceDate,ShiftCode,RowID = ROW_NUMBER() OVER (
ORDER BY EmpNo, AttendanceDate
), WeekNo = 1 into #RawData FROM -- My table
-- Step 2 : Use temp table
;WITH FinalData
AS (
SELECT EmpNo, AttendanceDate, ShiftCode, RowID, WeekNo = 1
FROM #RawData DA
WHERE RowID = 1
UNION ALL
SELECT DA.EmpNo, DA.AttendanceDate, DA.ShiftCode, DA.RowID,
WeekNo = (CASE WHEN FinalData.EmpNo != DA.EmpNo THEN 1 ELSE FinalData.WeekNo + (CASE WHEN (FinalData.ShiftCode = 'WO' AND DA.ShiftCode != 'WO') THEN 1 ELSE 0 END) END)
FROM FinalData
INNER JOIN #RawData DA ON DA.RowID = FinalData.RowID + 1
)
SELECT EmpNo, MIN(AttendanceDate) AS StartDate, MAX(AttendanceDate) AS EndDate, WeekNo
FROM FinalData
GROUP BY EmpNo, WeekNo
ORDER BY EmpNo, WeekNo
OPTION (MAXRECURSION 0)
I have a table like below.
CREATE TABLE #Test
(
CustId INT ,
CustName VARCHAR(100) ,
CustHeading VARCHAR(100)
)
INSERT INTO #Test
SELECT '1','john carroll','Heading 1'
UNION ALL
SELECT '1','john carroll','Heading 2'
UNION ALL
SELECT '2','john c','Heading 1'
UNION ALL
SELECT '2','john c','Heading 2'
UNION ALL
SELECT '2','john c','Heading 3'
UNION ALL
SELECT '3','john lynch','Heading 1'
UNION ALL
SELECT '4','john carroll lynch','Heading 1'
UNION ALL
SELECT '4','john carroll lynch','Heading 4'
UNION ALL
SELECT '4','john carroll lynch','Heading 5'
UNION ALL
SELECT '5','john c lynch','Heading 1'
UNION ALL
SELECT '5','john c lynch','Heading 3'
UNION ALL
SELECT '6','john c l','Heading 11'
UNION ALL
SELECT '6','john c l','Heading 12'
UNION ALL
SELECT '7','john c ln','Heading 1'
UNION ALL
SELECT '7','john c ln','Heading 2'
UNION ALL
SELECT '2','john c','Heading 11'
UNION ALL
SELECT '2','john c','Heading 12'
In this, we need to group the customers who are having atleast two matching headings among them.
For example,custID :: 1,2 and 7 are having two matching CustHeading :: Header 1 and Header 2, so they are grouped.CustID :: 2 and 5 having two matching CustHeading :: Header 1 and Header 3,they also can be grouped. Please let me know how to achieve this
without using WHILE loop
Thanks in advance.
SELECT
DISTINCT
CASE WHEN c.num = 1 THEN a.CustId
ELSE a.[_CustId]
END ,
CASE WHEN c.num = 1 THEN a.CustName
ELSE a.[_CustName]
END ,
'M' + CAST(DENSE_RANK() OVER ( ORDER BY mx, mn ) AS VARCHAR(100)) AS gr_nbr
FROM
(
SELECT
a.CustId ,
a.CustName ,
c.CustId _CustId ,
c.CustName _CustName,
MAX(a.CustHeading) mx,
MIN(a.CustHeading) mn
FROM
#Test a
JOIN #Test c ON c.CustHeading = a.CustHeading
AND c.CustId > a.CustId
GROUP BY
a.CustId ,
a.CustName ,
c.CustId ,
c.CustName
HAVING
MAX(a.CustHeading) <> MIN(a.CustHeading)
) a
JOIN #Test b ON b.CustId = a.[_CustId]
CROSS JOIN (
SELECT
1 num
UNION ALL
SELECT
2 num
) AS c
ORDER BY
3 ,
1
Sorry my brain is not working today. I'm sure there is a simpler way to do this but I can think right now. This seems to work fine for me. Let me know if it needs any changes.
CREATE TABLE #Test
(
CustId INT ,
CustName VARCHAR(100) ,
CustHeading VARCHAR(100)
)
INSERT INTO #Test
SELECT '1','john carroll','Heading 1'
UNION ALL
SELECT '1','john carroll','Heading 2'
UNION ALL
SELECT '2','john c','Heading 1'
UNION ALL
SELECT '2','john c','Heading 2'
UNION ALL
SELECT '2','john c','Heading 3'
UNION ALL
SELECT '3','john lynch','Heading 1'
UNION ALL
SELECT '4','john carroll lynch','Heading 1'
UNION ALL
SELECT '4','john carroll lynch','Heading 4'
UNION ALL
SELECT '4','john carroll lynch','Heading 5'
UNION ALL
SELECT '5','john c lynch','Heading 1'
UNION ALL
SELECT '5','john c lynch','Heading 3'
UNION ALL
SELECT '6','john c l','Heading 11'
UNION ALL
SELECT '6','john c l','Heading 12'
UNION ALL
SELECT '7','john c ln','Heading 1'
UNION ALL
SELECT '7','john c ln','Heading 2'
UNION ALL
SELECT '2','john c','Heading 11'
UNION ALL
SELECT '2','john c','Heading 12';
WITH CTE_Heading
AS
(
SELECT DISTINCT custHeading
FROM #Test
),
CTE_Paired_Headings
AS
(
SELECT A.custHeading AS Head1,
B.CustHeading AS Head2
FROM CTE_Heading A
INNER JOIN CTE_Heading B
ON A.custHeading < B.custHeading
),
CTE_Matching_Cust
AS
(
SELECT A.Head1,
A.Head2,
B.CustId,
B.CustName
FROM CTE_Paired_Headings A
INNER JOIN #Test B
ON A.Head1 = B.CustHeading
OR A.Head2 = B.CustHeading
GROUP BY A.Head1,A.Head2,B.CustId,B.CustName
HAVING COUNT(*) >= 2
),
CTE_HeadingGroups
AS
(
SELECT 'M' + CAST(ROW_NUMBER() OVER (ORDER BY Head1,Head2) AS VARCHAR(5)) MatchingID,
Head1,
Head2
FROM CTE_Matching_Cust
GROUP BY Head1,Head2
HAVING COUNT(*) >= 2
)
SELECT B.CustId,
B.CustName,
A.MatchingID
FROM CTE_HeadingGroups A
INNER JOIN CTE_Matching_Cust B
ON A.Head1 = B.Head1
AND A.Head2 = B.Head2
ORDER BY 3,1
DROP TABLE #Test
Results:
CustId CustName MatchingID
-----------------------------------------------
1 john carroll M1
2 john c M1
7 john c ln M1
2 john c M2
5 john c lynch M2
2 john c M3
6 john c l M3