SELECT Value even if NULL on LEFT JOIN - tsql

I am trying to pull data out and chuck it into a Stimulsoft report. The problem I am having is that I need it to output to two columns. I also need every "manager" record to show even if the count assigned to said record is NULL.
This is what i have at the moment:
DECLARE #ManagerCount INT = (( SELECT Count(*) FROM AM WHERE dbo.AM.AMCurrent = 1 AND dbo.AM.OmitInReport = 0 ) + 1) / 2
DECLARE #tmp_AM1 TABLE (AMID INT, AMName NVARCHAR(100), ID INT)
INSERT INTO #tmp_AM1 SELECT AMID, AMName, row_number() over (order by AMID ) FROM AM
WHERE dbo.AM.AMCurrent = 1 AND dbo.AM.OmitInReport = 0
SELECT * FROM (
SELECT ta.id AS id1, ta.AMName AS ManagerName1, COUNT(*) AS ManagerCount1 FROM #tmp_AM1 tA INNER JOIN Job J ON tA.AMID = j.AMID
WHERE ta.ID BETWEEN 1 AND #ManagerCount AND j.jobStatusID != 5
GROUP BY ta.ID, ta.AMName
) a
LEFT JOIN
(
SELECT ta.id AS id2,ta.AMName AS ManagerName2, COUNT(*) AS ManagerCount2 FROM #tmp_AM1 tA INNER JOIN Job J ON tA.AMID = j.AMID
WHERE ta.ID > #ManagerCount AND j.jobStatusID != 5
GROUP BY ta.AMName, ta.ID
) b ON a.id1 + #ManagerCount = b.id2
Which ends up returning something like:
There are 18 managers so 9 per column, but this code doesn't show them all since anything that doesn't have a count in the first left join, won't show, and therefore the same row in column 2 doesn't show.
Results of SELECT * FROM #tmp_AM1:

DECLARE #tmp_AM1 TABLE (AMID INT, AMName NVARCHAR(100), ID INT)
INSERT INTO #tmp_AM1 SELECT AMID, AMName, row_number() over (order by AMID ) FROM AM
WHERE dbo.AM.AMCurrent = 1 AND dbo.AM.OmitInReport = 0
SELECT * FROM (
SELECT ta.id AS id1, ta.AMName AS ManagerName1, COUNT(*) AS ManagerCount1 FROM #tmp_AM1 tA INNER JOIN Job J ON tA.AMID = j.AMID
WHERE ta.ID BETWEEN 1 AND #ManagerCount AND j.jobStatusID != 5
GROUP BY ta.ID, ta.AMName
) a
LEFT OUTER JOIN
(
SELECT ta.id AS id2,ta.AMName AS ManagerName2, COUNT(*) AS ManagerCount2 FROM #tmp_AM1 tA INNER JOIN Job J ON tA.AMID = j.AMID
WHERE ta.ID > #ManagerCount AND j.jobStatusID != 5
GROUP BY ta.AMName, ta.ID
) b ON a.id1 + #ManagerCount = b.id2 where ManagerName2 IS Null and ManagerCount2 IS NULL
just you want to use LEFT OUTER JOIN for select row even there is have any null values.,

Since the two subqueries are pretty much identical, except the where-statement, I would consiter rewriting it into one single query. I'm not sure why you need the same columns outputed into different columns in the result, but something like this might work:
WITH cte AS (
SELECT
ta.id AS id
,ta.AMName AS ManagerName
,COUNT(*) AS ManagerCount
,CASE WHEN ta.ID BETWEEN 1 AND #ManagerCount THEN 0 ELSE 1 END AS something
FROM
#tmp_AM1 tA
INNER JOIN Job J ON tA.AMID = j.AMID
WHERE
j.jobStatusID != 5
GROUP BY
ta.ID
,ta.AMName
,CASE WHEN ta.ID BETWEEN 1 AND #ManagerCount THEN 0 ELSE 1 END
)
SELECT
CASE WHEN something = 0 THEN cte.id ELSE null END AS id1
,CASE WHEN something = 0 THEN cte.ManagerName ELSE null END AS ManagerName1
,CASE WHEN something = 0 THEN cte.ManagerCount ELSE null END AS ManagerCount1
,CASE WHEN something = 1 THEN cte.id ELSE null END AS id2
,CASE WHEN something = 1 THEN cte.ManagerName ELSE null END AS ManagerName2
,CASE WHEN something = 1 THEN cte.ManagerCount ELSE null END AS ManagerCount2
FROM
cte

Probably not the best approach but i got the correct output using:
DECLARE #ManagerCount INT = (( SELECT Count(*) FROM AM WHERE dbo.AM.AMCurrent = 1 AND dbo.AM.OmitInReport = 0 ) + 1) / 2
DECLARE #tmp_AM1 TABLE (AMID INT, AMName NVARCHAR(100), ID INT)
INSERT INTO #tmp_AM1 SELECT AMID, AMName, row_number() over (order by AMID ) FROM AM
WHERE dbo.AM.AMCurrent = 1 AND dbo.AM.OmitInReport = 0
ORDER By AMName
SELECT ManagerName1, ManagerName2, ManagerCount1, ManagerCount2 FROM (
SELECT AMID, ta.id AS id1, ta.AMName AS ManagerName1 FROM #tmp_AM1 tA
WHERE (ta.ID BETWEEN 1 AND #ManagerCount)
) a
LEFT JOIN
(
SELECT AMID, ISNULL(COUNT(*), 0) AS ManagerCount1 FROM Job j
INNER JOIN tblJobOutcome jO ON j.JobOutcomeID = jo.JobOutcomeID AND jO.JobOutcomeID != 5
GROUP BY AMID
) a1 ON a.AMID = a1.AMID
LEFT JOIN
(
SELECT AMID, ta.id AS id2, ta.AMName AS ManagerName2 FROM #tmp_AM1 tA
WHERE (ta.ID > #ManagerCount)
) b ON a.id1 + #ManagerCount = b.id2
LEFT JOIN
(
SELECT AMID, ISNULL(COUNT(*), 0) AS ManagerCount2 FROM Job j
INNER JOIN tblJobOutcome jO ON j.JobOutcomeID = jo.JobOutcomeID AND jO.JobOutcomeID != 5
GROUP BY AMID
) b1 ON b.AMID = b1.AMID
Gives me the correct output in two columns.
gives me this:

Related

Postgresql nested select max(sum())

My Query:
select table.attribute, count(table.attribute) AS cnt from table
group by table.attribute
order by cnt desc;
The output is something like:
attribute | cnt
-----------+-----
A | 2
B | 2
G | 1
F | 1
But i only want the max values (A & B).
You can do this with a single level of nesting:
select attribute,
cnt
from (
select attribute,
count(*) AS cnt,
max(count(*)) over () as max_cnt
from t
group by attribute
) t
where cnt = max_cnt;
You can use the power of CTE to achieve this:
WITH count_values
AS (SELECT table.attribute,
Count(table.attribute) AS cnt
FROM table
GROUP BY table.attribute),
max_values
AS (SELECT Max(cnt) AS max_cnt
FROM (SELECT cnt
FROM count_values) sub)
SELECT *
FROM count_values cv
JOIN max_values mv
ON mv.max_cnt = cv.cnt;
you can use rank as below
with cte as (
select *, Rank() over(order by cnt desc) as rnk from yourattribute
) select * from cte where rnk = 1

sql recursion: find tree given middle node

I need to get a tree of related nodes given a certain node, but not necessary top node. I've got a solution using two CTEs, since I am struggling to squeeze it all into one CTE :). Might somebody have a sleek solution to avoid using two CTEs? Here is some code that I was playing with:
DECLARE #temp AS TABLE (ID INT, ParentID INT)
INSERT INTO #temp
SELECT 1 ID, NULL AS ParentID
UNION ALL
SELECT 2, 1
UNION ALL
SELECT 3, 2
UNION ALL
SELECT 4, 3
UNION ALL
SELECT 5, 4
UNION ALL
SELECT 6, NULL
UNION ALL
SELECT 7, 6
UNION ALL
SELECT 8, 7
DECLARE #startNode INT = 4
;WITH TheTree (ID,ParentID)
AS (
SELECT ID, ParentID
FROM #temp
WHERE ID = #startNode
UNION ALL
SELECT t.id, t.ParentID
FROM #temp t
JOIN TheTree tr ON t.ParentID = tr.ID
)
SELECT * FROM TheTree
;WITH Up(ID,ParentID)
AS (
SELECT t.id, t.ParentID
FROM #temp t
WHERE t.ID = #startNode
UNION ALL
SELECT t.id, t.ParentID
FROM #temp t
JOIN Up c ON t.id = c.ParentID
)
--SELECT * FROM Up
,TheTree (ID,ParentID)
AS (
SELECT ID, ParentID
FROM Up
WHERE ParentID is null
UNION ALL
SELECT t.id, t.ParentID
FROM #temp t
JOIN TheTree tr ON t.ParentID = tr.ID
)
SELECT * FROM TheTree
thanks
Meh. This avoids using two CTEs, but the result is a brute force kludge that hardly qualifies as "sleek" as it won’t be efficient if your table is at all sizeable. It will:
Recursively build all possible hierarchies
As you build them, flag the target NodeId as you find it
Return only the targeted tree
I threw in column “TreeNumber” on the off-chance the TargetId appears in multiple hierarchies, or if you’d ever have multiple values to check in one pass. “Depth” was added to make the output a bit more legible.
A more complex solution like #John’s might do, and more and subtler tricks could be done with more detailed table sturctures.
DECLARE #startNode INT = 4
;WITH cteAllTrees (TreeNumber, Depth, ID, ParentID, ContainsTarget)
AS (
SELECT
row_number() over (order by ID) TreeNumber
,1
,ID
,ParentID
,case
when ID = #startNode then 1
else 0
end ContainsTarget
FROM #temp
WHERE ParentId is null
UNION ALL
SELECT
tr.TreeNumber
,tr.Depth + 1
,t.id
,t.ParentID
,case
when tr.ContainsTarget = 1 then 1
when t.ID = #startNode then 1
else 0
end ContainsTarget
FROM #temp t
INNER JOIN cteAllTrees tr
ON t.ParentID = tr.ID
)
SELECT
TreeNumber
,Depth
,ID
,ParentId
from cteAllTrees
where TreeNumber in (select TreeNumber from cteAllTrees where ContainsTarget = 1)
order by
TreeNumber
,Depth
,ID
Here is a technique where you can select the entire hierarchy, a specific node with all its children, and even a filtered list and how they roll.
Note: See the comments next to the DECLAREs
Declare #YourTable table (id int,pt int,name varchar(50))
Insert into #YourTable values
(1,null,'1'),(2,1,'2'),(3,1,'3'),(4,2,'4'),(5,2,'5'),(6,3,'6'),(7,null,'7'),(8,7,'8')
Declare #Top int = null --<< Sets top of Hier Try 2
Declare #Nest varchar(25) = '|-----' --<< Optional: Added for readability
Declare #Filter varchar(25) = '' --<< Empty for All or try 4,6
;with cteP as (
Select Seq = cast(1000+Row_Number() over (Order by name) as varchar(500))
,ID
,pt
,Lvl=1
,name
From #YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(pt,-1) else ID end
Union All
Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.name)) as varchar(500))
,r.ID
,r.pt
,p.Lvl+1
,r.name
From #YourTable r
Join cteP p on r.pt = p.ID)
,cteR1 as (Select *,R1=Row_Number() over (Order By Seq) From cteP)
,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select Distinct
A.R1
,B.R2
,A.ID
,A.pt
,A.Lvl
,name = Replicate(#Nest,A.Lvl-1) + A.name
From cteR1 A
Join cteR2 B on A.ID=B.ID
Join (Select R1 From cteR1 where IIF(#Filter='',1,0)+CharIndex(concat(',',ID,','),concat(',',#Filter+','))>0) F on F.R1 between A.R1 and B.R2
Order By A.R1

Exlude row with same key from 1st select if exists in 2nd select statement in UNION

I have two tables Part and Service Entry Part. The first statement returns all parts and the second statement returns parts with a service type of 13.
What I need to do is if the second select returns a part then the record from second select should be inlcuded and that part from the first select should be be excluded
DECLARE #run_log TABLE(
[Instrument] nvarchar(max),
[SubSystem] NVARCHAR(max),
[AbPartNumber] NVARCHAR(max),
[RSLMSPartID] int,
[PartDescriptionWithParent] NVARCHAR(max),
[PartRevisionNumber] NVARCHAR(max),
[ServiceEntryID] int,
[Date] datetime,
[TSBNumber] nvarchar (max),
[CRNumber] nvarchar(max)
)
insert #run_log
-- parts with default revision number
select
System.SystemFullName as Instrument,
Part.System as SubSystem,
Part.AbPartNo as AbPartNumber,
Part.ID as RSLMSPartID,
Part.PartDescriptionWithParent,
Part.RevisionNumber as PartRevisionNumber,
NULL as ServiceEntryID,
NULL as Date,
NULL as TSBNumber,
NULL as CRNumber
from Part
inner join InstrumentType on Part.InstrumentTypeID = InstrumentType.ID
inner join SystemModule on SystemModule.InstrumentTypeID = Part.InstrumentTypeID
inner join System on System.ID = SystemModule.SystemID
WHERE ((#PlatformID =0) OR (System.PlatformID = #PlatformID) OR (#PlatformID = 12 AND System.PlatformID <= 2))
AND (#SelectedSystemID is null OR System.ID IN(select * from dbo.SplitInts_RBAR_1(#SelectedSystemID, ',')))
AND (#SelectedAbIDs is null OR Part.ID IN (select * from dbo.SplitInts_RBAR_1(#SelectedAbIDs,',')))
AND (#SelectedSubSystems is null OR Part.System IN (select * from dbo.SplitStrings_Moden(#SelectedSubSystems,',')))
AND System.Active = 1 and Part.Active = 1
ORDER BY SubSystem,RSLMSPartID
;WITH RunLogs AS(
SELECT *
FROM #run_log r
)
select * from RunLogs
UNION
select System.SystemFullName as Instrument,
Part.System as Subsystem,
Part.AbbottPartNo as AbPartNumber,
Part.ID as RSLMSPartID,
Part.PartDescriptionWithParent,
ServiceEntryPart.PartRevisionNumber AS PartRevisionNumber,
ServiceEntryPart.ServiceEntryID,
ServiceEntry.ServiceDateTime as Date,
ServiceEntry.TSBNumber,
ServiceEntry.CRNumber
from Part
inner join ServiceEntryPart on ServiceEntryPart.PartID = Part.ID
inner join ServiceEntry on ServiceEntry.ID = ServiceEntryPart.ServiceEntryID
inner join systemmodule on ServiceEntryPart.SystemModuleID = SystemModule.ID
inner join System on System.ID = SystemModule.SystemID
cross apply
dbo.SplitStrings_Moden(ServiceEntryPart.ServiceTypeIDs, N',') M2
JOIN dbo.SplitStrings_Moden('13', N',') P ON (M2.Item = P.Item)
WHERE System.Active = 1 AND Part.Active = 1
AND (#SelectedSystemID is null OR System.ID IN(select * from dbo.SplitInts_RBAR_1(#SelectedSystemID, ',')))
AND ((#PlatformID =0) OR (System.PlatformID = #PlatformID) OR (#PlatformID = 12 AND System.PlatformID <= 2))
AND (ServiceEntry.ServiceDateTime between #StartDate and #EndDate)
AND (#SelectedAbIDs is null OR Part.ID in (select * from dbo.SplitInts_RBAR_1(#SelectedAbIDs,',')))
AND (#SelectedSubSystems is null OR Part.System IN (select * from dbo.SplitStrings_Moden(#SelectedSubSystems,',')))

SQL Running Subtraction and Deviation

-- Just a brief of business scenario is table has been created for a good receipt.
-- So here we have good expected line with PurchaseOrder(PO) in first few line.
-- And then we receive each expected line physically and that time these quantity may be different
-- due to business case like quantity may damage and short quantity like that.
-- So we maintain a status for that eg: OK, Damage, also we have to calculate short quantity
-- based on total of expected quantity of each item and total of received line.
if object_id('DEV..Temp','U') is not null
drop table Temp
CREATE TABLE Temp
(
ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
Item VARCHAR(32),
PO VARCHAR(32) NULL,
ExpectedQty INT NULL,
ReceivedQty INT NULL,
[STATUS] VARCHAR(32) NULL,
BoxName VARCHAR(32) NULL
)
-- Please see first few line with PO data will be the expected lines,
-- and then rest line will be received line
INSERT INTO TEMP (Item,PO,ExpectedQty,ReceivedQty,[STATUS],BoxName)
SELECT 'ITEM01','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01','PO-02','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM02','PO-01','40',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-01','50',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-02','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-03','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM04','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01',NULL,NULL,'20','OK','box01' UNION ALL
SELECT 'ITEM01',NULL,NULL,'25','OK','box02' UNION ALL
SELECT 'ITEM01',NULL,NULL,'5','DAMAGE','box03' UNION ALL
SELECT 'ITEM02',NULL,NULL,'38','OK','box04' UNION ALL
SELECT 'ITEM02',NULL,NULL,'2','DAMAGE','box05' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box06' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box07' UNION ALL
SELECT 'ITEM03',NULL,NULL,'10','DAMAGE','box09' UNION ALL
SELECT 'ITEM04',NULL,NULL,'25','OK','box10'
-- Below Table is my expected result based on above data.
-- I need to show those data following way.
-- So I appreciate if you can give me an appropriate query for it.
-- Note: first row is blank and it is actually my table header. :)
-- Conditions : any of row, we cant have ReceivedQty, DamageQty and ShortQty
-- values more than ExpectedQty value. Item03 has this scenario
-- Query should run in SQL 2000 DB
SELECT ''as'ITEM', ''as'PO#', ''as'ExpectedQty',''as'ReceivedQty',''as'DamageQty' ,''as'ShortQty' UNION ALL
SELECT 'ITEM01','PO-01','30','30','0' ,'0' UNION ALL
SELECT 'ITEM01','PO-02','20','15','5' ,'0' UNION ALL
SELECT 'ITEM02','PO-01','40','38','2' ,'0' UNION ALL
SELECT 'ITEM03','PO-01','50','50','0' ,'0' UNION ALL
SELECT 'ITEM03','PO-02','30','20','10' ,'10' UNION ALL
SELECT 'ITEM03','PO-03','20','0','0','20' UNION ALL
SELECT 'ITEM04','PO-01','30','25','0' ,'5'
Using this solution as a starting point, I've eventually ended up with this:
SELECT
Item,
PO,
ExpectedQty,
ReceivedQty = CASE
WHEN RemainderQty >= 0 THEN ExpectedQty
WHEN RemainderQty < -ExpectedQty THEN 0
ELSE RemainderQty + ExpectedQty
END,
DamageQty = CASE
WHEN RemainderQty >=0 OR ExpectedQty < -TotalRemainderQty THEN 0
WHEN RemainderQty < -ExpectedQty AND TotalRemainderQty > 0 THEN ExpectedQty
WHEN RemainderQty < -ExpectedQty AND TotalRemainderQty < -DamagedQty THEN ExpectedQty + TotalRemainderQty
WHEN RemainderQty > -DamagedQty THEN -RemainderQty
ELSE DamagedQty
END,
ShortQty = CASE
WHEN TotalRemainderQty >= 0 THEN 0
WHEN TotalRemainderQty < -ExpectedQty THEN ExpectedQty
ELSE -TotalRemainderQty
END
FROM (
SELECT
a.Item,
a.PO,
a.ExpectedQty,
b.DamagedQty,
RemainderQty = b.ReceivedQty - a.RunningTotalQty,
TotalRemainderQty = b.ReceivedQty + b.DamagedQty - a.RunningTotalQty
FROM (
SELECT
a.Item,
a.PO,
a.ExpectedQty,
RunningTotalQty = SUM(a2.ExpectedQty)
FROM (SELECT Item, PO, ExpectedQty FROM Temp WHERE STATUS IS NULL) AS a
INNER JOIN (SELECT Item, PO, ExpectedQty FROM Temp WHERE STATUS IS NULL) AS a2
ON a.Item = a2.Item AND a.PO >= a2.PO
GROUP BY
a.Item,
a.PO,
a.ExpectedQty
) a
LEFT JOIN (
SELECT
Item,
ReceivedQty = SUM(CASE STATUS WHEN 'OK' THEN ReceivedQty ELSE 0 END),
DamagedQty = SUM(CASE STATUS WHEN 'DAMAGE' THEN ReceivedQty ELSE 0 END)
FROM Temp
GROUP BY Item
) b ON a.Item = b.Item
) s;

How to use ROW_NUMBER in the following procedure?

I have the following stored procedure which returns A, B, and the count in descending order. I am trying to use ROW_NUMBER, so I can page the records, but I want the first row number 1 to be the record with the highest count, so basically, if I return a table with 3 records and the count is 30, 20, 10, then row number 1 should correspond with count 30, row number 2 should correspond with count 20, and row number 3 should correspond with count 10. dbo.f_GetCount is a function that returns a count.
create procedure dbo.Test
as
#A nvarchar(300) = NULL,
#B nvarchar(10) = NULL
as
select #A = nullif(#A,'')
,#B = nullif(#B,'');
select h.A
,hrl.B
,dbo.f_GetCount(hrl.A,h.B) as cnt
from dbo.hrl
inner join dbo.h
on h.C = hrl.C
where(#A is null
or h.A like '%'+#A+'%'
)
and (#B is null
or hrl.B = #B
)
group by hrl.B
,h.A
order by cnt desc;
WITH q AS
(
SELECT h.A, hrl.B,
dbo.f_GetCount(hrl.A,h.B) as cnt
FROM dbo.hrl
INNER JOIN dbo.h on h.C = hrl.C
WHERE (#A IS NULL OR h.A like '%' + #A + '%')
AND (#B IS NULL OR hrl.B = #B)
GROUP BY hrl.B, h.A
)
SELECT q.*, ROW_NUMBER() OVER (ORDER BY cnt DESC) AS rn
FROM q
ORDER BY rn DESC
To retrieve first 10 rows, use:
WITH q AS
(
SELECT h.A, hrl.B,
dbo.f_GetCount(hrl.A,h.B) as cnt
FROM dbo.hrl
INNER JOIN dbo.h on h.C = hrl.C
WHERE (#A IS NULL OR h.A like '%' + #A + '%')
AND (#B IS NULL OR hrl.B = #B)
GROUP BY hrl.B, h.A
)
SELECT TOP 10 q.*,
ROW_NUMBER() OVER (ORDER BY cnt DESC, A, B) AS rn
FROM q
ORDER BY cnt DESC, A, B
To retrieve rows between 11 and 20, use:
SELECT *
FROM (
WITH q AS
(
SELECT h.A, hrl.B,
dbo.f_GetCount(hrl.A,h.B) as cnt
FROM dbo.hrl
INNER JOIN dbo.h on h.C = hrl.C
WHERE (#A IS NULL OR h.A like '%' + #A + '%')
AND (#B IS NULL OR hrl.B = #B)
GROUP BY hrl.B, h.A
)
SELECT q.*,
ROW_NUMBER() OVER (ORDER BY cnt DESC, A, B) AS rn
FROM q
) qq
WHERE rn BETWEEN 11 AND 20
ORDER BY cnt DESC, A, B
I would use a sub-query to get the values of the function into the result, and then the ROW_NUMBER ranking function, like so:
select
ROW_NUMBER() over (order by t.cnt desc) as RowId, t.*
from
(
SELECT
h.A, hrl.B, dbo.f_GetCount(hrl.A,h.B) as cnt
FROM
dbo.hrl
INNER JOIN dbo.h on h.C = hrl.C
WHERE
(#A IS NULL OR h.A like '%' + #A + '%') AND
(#B IS NULL OR hrl.B = #B)
GROUP BY
hrl.B, h.A
) as t
order by
1
If you wanted only a certain section of results (say, for paging), then you would need another subquery, and then filter on the row number:
select
t.*
from
(
select
ROW_NUMBER() over (order by t.cnt desc) as RowId, t.*
from
(
SELECT
h.A, hrl.B, dbo.f_GetCount(hrl.A,h.B) as cnt
FROM
dbo.hrl
INNER JOIN dbo.h on h.C = hrl.C
WHERE
(#A IS NULL OR h.A like '%' + #A + '%') AND
(#B IS NULL OR hrl.B = #B)
GROUP BY
hrl.B, h.A
) as t
) as t
where
t.RowId between 1 and 10
order by
t.RowId
Note that in this query, you could put ROW_NUMBER anywhere in the select list, since you are no longer reliant on using the "order by 1" syntax for the order by statement.
There is a subtle issue here when calling this query multiple times. It is not guaranteed that the order in which the records are returned are going to be consistent if the number of items in each group is not unique. In order to address this, you have to change the ROW_NUMBER function to order on the fields that make up the group in the count.
In this case, it would be A and B, resulting in:
select
t.*
from
(
select
ROW_NUMBER() over (order by t.cnt desc, t.A, t.B) as RowId, t.*
from
(
SELECT
h.A, hrl.B, dbo.f_GetCount(hrl.A,h.B) as cnt
FROM
dbo.hrl
INNER JOIN dbo.h on h.C = hrl.C
WHERE
(#A IS NULL OR h.A like '%' + #A + '%') AND
(#B IS NULL OR hrl.B = #B)
GROUP BY
hrl.B, h.A
) as t
) as t
where
t.RowId between 1 and 10
order by
t.RowId
This ends up ordering the results consistently between calls when the count of the items between groups is not unique (assuming the same set of data).
SELECT h.A, hrl.B,
dbo.f_GetCount(hrl.A,h.B) as cnt,
ROW_NUMBER() over (order by cnt desc) as row_num
FROM dbo.hrl
INNER JOIN dbo.h on h.C = hrl.C
WHERE (#A IS NULL OR h.A like '%' + #A + '%')
AND (#B IS NULL OR hrl.B = #B)
GROUP BY hrl.B, h.A
ORDER BY cnt desc
This should do the trick. I don't have SSMS in front of me to test, but you MAY have to substitute the usage of 'cnt' in the ROW_NUMBER's order by clause with a second call to the function, but this should give you the general idea.