Julian Drago
Julian Drago

Reputation: 749

SQL Generate a table of multiple top X records, where top # comes from another table

I'm using SQL Server 2012.

I need to generate an output that includes the results for multiple "top" queries, where the # of "top" rows returned in each query varies based on the value in another table.

My first table, call it Table1, is the reference table where NUMBER tells me how many top rows I need to return from another table for each unique MARKET/MEASURE.

MARKET      MEASURE     NUMBER
------      -------      ------
MarketA     MeasureA    411
MarketA     MeasureB    396
MarketB     MeasureA    548
MarketB     MeasureC    424
MarketC     MeasureC    411

The second table, Table2, lists detail for each individual person in a given MARKET/MEASURE, where the combination of MARKET and MEASURE is my primary key. There are many entries for any given MARKET/MEASURE.

MARKET      MEASURE      LASTNAME      COMPLIANT
------      -------      --------      ---------
MarketA     MeasureA     Coppola       Y
MarketA     MeasureA     Winterbottom  N
MarketA     MeasureB     Scorsese      Y
MarketC     MeasureC     Tarr          Y

For each value in Table1, I need to return that many top rows from Table2 based on a sort of LASTNAME in ascending order. So for example, because Table1 has a NUMBER of 411 for MarketA/MeasureA, my output needs to contain the TOP 411 * rows from Table2 (based on all persons in that market sorted by LASTNAME ascending), and the TOP 396 rows for MarketA/MeasureB, and then the TOP 548 rows for MarketB/MeasureA, and so on, all in one table, as if I've "UNIONed" (?) each query individually.

How do I do this dynamically without having to UNION individual queries for each MARKET/MEASURE in Table1 (of which there are over 1000)?

I feel like the answer is to use a select expression to generate # in the TOP expression, like.....

select TOP (select NUMBER from TABLE2) *  
from TABLE1 t1
inner join TABLE2 t2 on t2.MARKET = T1.MARKET
                     and t2.MEASURE = T2.MEASURE 

...but obviously I'm missing a few steps because the TOP expression will bring back multiple values from TABLE2, and I can't quite figure out how to get it to "run" for each MARKET/MEASURE combination.

Help much appreciated.

Upvotes: 1

Views: 69

Answers (2)

Julian Drago
Julian Drago

Reputation: 749

For anyone in the same predicament, I figured out another way of accomplishing the same thing, using the ROW_NUMBER() windowed function instead of trying to use TOP dynamically. The idea was to add a row number to Table2, partitioned by market & measure and ordered by last name, then to join the Table1.Number to Table2, and select only rows where the row number was less than Table1.Number, thus returning the "top" number of rows according to the Table1.Number.

with temp as (select 'rownum' = row_number() over(partition by t2.market, t2.measure)
                                                  order by t2.lastname)
                , t2.*
                , t1.number
              from table2 t2
              inner join table1 t1 on t1.market = t2.market
                                   and t1.measure = t2.measure)
select *
from temp
where rownum <= number
order by market, measure, rownum

Upvotes: 1

mhep
mhep

Reputation: 2139

This is what APPLY can be used for

SELECT
    *

FROM
    Table1
    CROSS APPLY
    (
        SELECT TOP (Table1.Number)
            *

        FROM
            Table2

        WHERE
            Table1.Market = Table2.Market
            AND Table1.Measure = Table2.Measure

        ORDER BY
            LastName
    ) AS TopResults

http://sqlfiddle.com/#!6/46b57/4

Upvotes: 2

Related Questions