views:

60

answers:

7

I need to write a TSQL procedure which would perform 4 selects and join results int one dataset. Can this be done?

P.S.

Selects are performed in a loop

EDIT

Here is the schema. I wan't to get N latest points from each Unit which belongs to SOME map.

Maps
   Id

Maps2Units
   MapId
   UnitId

Units
   Id

Points
   Id
   UnitId
   Timestamp
A: 

Use temporary table

limpalex
+2  A: 

rather than select in loops, I'd first try to refactor it into a single select to pull back all the information.

Outside of that, dump all the select information into a temporary table (or a table variable, depending on performance impact) and then select from that table to return all the information is one statement.

Agent_9191
I already tried to squeeze it into one select but couldn't do it. I need to group select top N entries from M groups. Imagine I have table Species and Animals and I would like to select top 5 animals from each specie. If this possible I would appreciate you giving some advice. Then I would abandon separate selects
Sergej Andrejev
A: 
select col1 from tableA
union
select col2 from tableB
union
select col3 from tableC
union
select col4 from tableD

The columns selected must match datatype

James Curran
A: 

If all 4 datasets share common values to link them into rows, use INNER JOIN.

If all 4 datasets have the same column definitions, but just retrieve different data, use UNION

EDIT - based on your updated question, sounds like you need something like the following: http://allenbrowne.com/subquery-01.html#TopN to get top N per group

Kris C
A: 

Declare a temp (#) table or a table variable (@) and select into it. then just select * from your temp when you are done.

Sky Sanders
+2  A: 

Edit: another solution using top

SELECT
    *
FROM
    Maps2Units MU
    CROSS APPLY
    (
    SELECT TOP (@n) --or TOP 3 etc
        *
    FROM
        Points P
    WHERE
        P.UnitId = MU.UnitId
    ) P2

Guessing from your previous comment...

;WITH myCTE AS 
(
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY P.UnitId ORDER BY P.Timestamp DESC) AS PointsRank
    FROM
        Points P
        JOIN
        Maps2Units MU ON P.UnitId = MU.UnitId
)
SELECT
    *
FROM
    myCTE
WHERE
    PointsRank <= @M;

Original:

;WITH myCTE AS 
(
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY SpeciesID ORDER BY AnimalID /* ? */) AS AnimalRank,
        DENSE_RANK() OVER (ORDER BY SpeciesID /* ? */) AS SpeciesRank
    FROM
        MyAnimals
)
SELECT
    *
FROM
    myCTE
WHERE
    SpeciesRank <= @M
    AND
    AnimalRank <= @N
gbn
Could you explain this a bit. I'm not that good with TSQL. Also how fast is this?
Sergej Andrejev
Basically, generate a list of ranking numbers that can be used to filter later. ROW_NUMBER gives you 1, 2, 3, per UnitId. The DENSE_RANK (deleted) would give you 1, 2, 3 over UnitID. That is, top n per group, top m groups.
gbn
Does it selects all rows from each group and then filters only first three? Because my Points table will be huge I would like to know how fast is this request comparing to separate selects with "TOP".
Sergej Andrejev
probably slower because all rows must be calculated...
gbn
If I understand this correctly it performs nested select for each map2unit row. Therefore it will probably a fraction faster than selects in a loop. Am I right?
Sergej Andrejev
perhaps: the optimiser should be able to work it out. Try it, of course... :-)
gbn
A: 

From the information you provided:

SELECT Id, NULL AS MapID, NULL AS UnitID, NULL AS Timestamp FROM Maps

union all

SELECT NULL AS ID, MapID, UnitID, NULL AS Timestamp FROM Maps2Units

union all

SELECT ID, NULL, NULL, NULL FROM Units

union all 

SELECT id, null AS MapID, UnitID, null AS Timestamp FROM Points
Ian Boyd