I am trying to figure out how to return the top 10 records for each group of Trans.TranSID.
SELECT a.ABID, a.ABName, t.TranSID, SUM(IIF(TranTypeID = 'CO', td.Qty * CAST(td.Price AS money) * - 1,
td.Qty * CAST(td.Price AS money))) AS TotalSales
FROM Trans t INNER JOIN
TransDetail td ON t.TranID = td.TranID INNER JOIN
ABook a ON t.TranABID = a.ABID
WHERE (t.TranDate BETWEEN CONVERT(DATETIME, '2012-01-01 00:00:00', 102) AND CONVERT(DATETIME, '2013-01-01 00:00:00', 102)) AND
t.TranTypeID in ('SO','CA','CO') AND (t.TranStatus <> 'V')
GROUP BY a.ABID, a.ABName, t.TranSID
HAVING (NOT (a.ABName LIKE '%cash%'))
ORDER BY t.TranSID, TotalSales Desc
I can add "TOP 10" to the select statement, but that gives me the top 10 accounts regardless of the group. There are 25 groups of Trans.TranSID and I'm trying to get the top 10 only for each group.
Selecting a top n records for each category from any table, can be done easily using row_number function which generates a sequential integer to each row within a partition of a result set.
Typically, these are accomplished using the TOP or LIMIT clause. Problem is, Top N result sets are limited to the highest values in the table, without any grouping. The GROUP BY clause can help with that, but it is limited to the single top result for each group.
For example, TOP(10) would return the top 10 rows from the full result set. Optional. If PERCENT is specified, then the top rows are based on a percentage of the total result set (as specfied by the top_value).
I think you're looking for ROW_NUMBER()
with a PARTITION BY
SELECT *
FROM (
SELECT
ROW_NUMBER() OVER(PARTITION BY t.TranSID ORDER BY t.TranSID, SUM(IIF(TranTypeID = 'CO', td.Qty * CAST(td.Price AS money) * - 1, td.Qty * CAST(td.Price AS money))) DESC) as RowNum,
a.ABID,
a.ABName,
t.TranSID,
SUM(IIF(TranTypeID = 'CO', td.Qty * CAST(td.Price AS money) * - 1, td.Qty * CAST(td.Price AS money))) AS TotalSales
FROM Trans t
INNER JOIN TransDetail td
ON t.TranID = td.TranID
INNER JOIN ABook a
ON t.TranABID = a.ABID
WHERE (t.TranDate BETWEEN CONVERT(DATETIME, '2012-01-01 00:00:00', 102) AND CONVERT(DATETIME, '2013-01-01 00:00:00', 102))
AND t.TranTypeID in ('SO','CA','CO')
AND (t.TranStatus <> 'V')
GROUP BY a.ABID, a.ABName, t.TranSID
HAVING (NOT (a.ABName LIKE '%cash%'))
) a
WHERE a.RowNum <=10
This will assign a row number to each record in the grouping (the column defined by the PARTITION
, going from 1 to n. From there, you can run a SELECT
on it to grab any number of records per group.
I'm not super familiar with t-sql specifically and unfortunately I don't have access to a t-sql database to test that this accomplishes your goal.
That said, I think this is one way you could accomplish it using a subquery and the ROW_NUMBER function.
SELECT
*
FROM (
SELECT
a.ABID
, a.ABName
, t.TranSID
, SUM(IFF(TranTypeID = 'CO'
, td.Qty * CAST(td.Price AS MONEY) * -1
, td.Qty * CAST(td.Price AS MONEY))) AS TotalSales
, ROW_NUMBER()
OVER(PARTITION BY t.TranSID
ORDER BY SUM(IFF(TranTypeID = 'CO'
, td.Qty * CAST(td.Price AS MONEY) * -1
, td.Qty * CAST(td.Price AS MONEY))) DESC) AS row
FROM
Trans t
INNER JOIN TransDetail td
ON t.TranID = td.TranID
INNER JOIN ABook a
ON t.TranABID = a.ABID
WHERE
(t.TranDate BETWEEN CONVERT(DATETIME, '2012-01-01 00:00:00', 102) AND CONVERT(DATETIME, '2013-01-01 00:00:00', 102))
AND t.TranTypeID in ('SO','CA','CO')
AND (t.TranStatus <> 'V')
GROUP BY
a.ABID
, a.ABName
, t.TranSID
HAVING
(NOT (a.ABName LIKE '%cash%'))
) q
WHERE
q.row <= 10
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With