Welcome to Sign in | Help
in Search

Join cu doar primul rand din tabela

Last post 09-02-2008, 9:09 AM by rsocol. 2 replies.
Sort Posts: Previous Next
  •  09-01-2008, 6:08 PM 5547

    Join cu doar primul rand din tabela

    Am o tabela A (id, A1, A2) si o tabela B (id_A, id_B, B1), B fiind legat de A. Vreau sa scot randul din A cu doar primul rand din B. Inainte (SQL 2000) foloseam un subquery cu TOP 1, mai recent (2005) am inceput sa folosesc CTE si ROW_NUMBER().

    Cred ca problema asta a mai fost intalnita si de altcineva, deci voi ce folositi (poate stiti o metoda mai buna)? Smile

    Filed under: ,
  •  09-02-2008, 8:52 AM 5548 in reply to 5547

    Re: Join cu doar primul rand din tabela

    Când ai o problemă de acest gen, e recomandabil să prezinţi structura tabelelor (sub formă de CREATE TABLE, inclusiv constraint-urile aferente) şi date de test (sub formă de INSERT INTO), de exemplu:

    CREATE TABLE A (
        ID_A int PRIMARY KEY,
        A1 varchar(10),
        A2 varchar(10)
    )

    CREATE TABLE B (
        ID_B int PRIMARY KEY,
        ID_A int NOT NULL REFERENCES IA,
        B1 varchar(10)
    )

    INSERT INTO A VALUES (1, 'X1', 'Y1')
    INSERT INTO A VALUES (2, 'X2', 'Y2')
    INSERT INTO A VALUES (3, 'X3', 'Y3')

    INSERT INTO B VALUES (1, 1, 'ABC')
    INSERT INTO B VALUES (4, 2, 'ABC')
    INSERT INTO B VALUES (2, 2, 'BBB')
    INSERT INTO B VALUES (3, 2, 'AAA')

    Atunci când spui "primul rând din tabela B" trebuie să precizezi în ce ordine vrei să consideri rândurile respective, deoarece rândurile dintr-o tabelă nu au o ordine predefinită (nu contează ordinea în care au fost adăugate). Presupunând că ordinea dorită este dată de coloana B1, rezultatul aşteptat ar putea fi următorul:

    ID_A        A1         A2         B1
    ----------- ---------- ---------- ----------
    1           X1         Y1         ABC
    2           X2         Y2         AAA
    3           X3         Y3         NULL

    Rezultatul de mai sus poate fi obţinut cu oricare din următoarele query-uri:

    SELECT A.*, (SELECT MIN(B1) FROM B WHERE B.ID_A=A.ID_A) B1 FROM A;

    SELECT A.*, (SELECT TOP 1 B1 FROM B WHERE B.ID_A=A.ID_A ORDER BY B1) B1 FROM A;

    SELECT A.*, MIN(B1) AS B1
    FROM A LEFT JOIN B ON B.ID_A=A.ID_A
    GROUP BY A.ID_A, A1, A2;

    SELECT A.*, X.B1 FROM A LEFT JOIN (
        SELECT ID_A, MIN(B1) B1 FROM B
        GROUP BY ID_A
    ) X ON A.ID_A=X.ID_A;

    SELECT A.*, X.B1 FROM A LEFT JOIN (
        SELECT B.*, ROW_NUMBER() OVER (PARTITION BY ID_A ORDER BY B1) N
        FROM B
    ) X ON A.ID_A=X.ID_A AND X.N=1;

    WITH X AS (
        SELECT B.*, ROW_NUMBER() OVER (PARTITION BY ID_A ORDER BY B1) N
        FROM B
    )
    SELECT A.*, X.B1 FROM A LEFT JOIN X ON A.ID_A=X.ID_A AND X.N=1

    Răzvan
  •  09-02-2008, 9:09 AM 5549 in reply to 5548

    Re: Join cu doar primul rand din tabela

    Dar hai să complicăm puţin problema şi să presupunem că ai mai multe coloane în tabela B:

    CREATE TABLE A (
        ID_A int PRIMARY KEY,
        A1 varchar(10),
        A2 varchar(10)
    )

    CREATE TABLE B (
        ID_B int PRIMARY KEY,
        ID_A int NOT NULL REFERENCES A,
        B1 varchar(10),
        B2 varchar(10)
    )

    INSERT INTO A VALUES (1, 'X1', 'Y1')
    INSERT INTO A VALUES (2, 'X2', 'Y2')
    INSERT INTO A VALUES (3, 'X3', 'Y3')

    INSERT INTO B VALUES (1, 1, 'ABC', '321')
    INSERT INTO B VALUES (4, 2, 'ABC', '321')
    INSERT INTO B VALUES (2, 2, 'BBB', '222')
    INSERT INTO B VALUES (3, 2, 'AAA', '333')

    În acest caz, să presupunem că rezultatul dorit ar fi:

    ID_A        A1         A2         B1         B2
    ----------- ---------- ---------- ---------- ----------
    1           X1         Y1         ABC        321
    2           X2         Y2         AAA        333
    3           X3         Y3         NULL       NULL
    4           X4         Y4         CCC        123

    (adică pentru fiecare rând din A, dorim primul rând din B, în ordinea dată de B1 şi B2)

    Dacă modificăm primul query de mai sus, astfel:

    SELECT A.*,
        (SELECT MIN(B1) FROM B WHERE B.ID_A=A.ID_A) B1,
        (SELECT MIN(B2) FROM B WHERE B.ID_A=A.ID_A) B2
    FROM A

    atunci obţinem alt rezultat:

    ID_A        A1         A2         B1         B2
    ----------- ---------- ---------- ---------- ----------
    1           X1         Y1         ABC        321
    2           X2         Y2         AAA        222
    3           X3         Y3         NULL       NULL
    4           X4         Y4         CCC        123

    pentru că la rândul cu ID_A=2 s-a luat cel mai mic B2, în loc să se ia B2-ul corespunzător rândului cu cel mai mic B1.

    Putem obţine rezultatul dorit cu unul din următoarele query-uri:

    SELECT X.*,
        (SELECT MIN(B2) FROM B WHERE B.ID_A=X.ID_A AND B.B1=X.B1) B2
    FROM (
        SELECT A.*,
            (SELECT MIN(B1) FROM B WHERE B.ID_A=A.ID_A) B1
        FROM A
    ) X

    SELECT A.*, B.B1, B.B2 FROM A LEFT JOIN B ON A.ID_A=B.ID_A
    AND B.ID_B=(
        SELECT TOP 1 ID_B FROM B WHERE A.ID_A=B.ID_A
        ORDER BY B1, B2
    )

    SELECT X.ID_A, X.A1, X.A2, B.B1, B.B2 FROM (
        SELECT A.*, (
            SELECT TOP 1 ID_B FROM B WHERE A.ID_A=B.ID_A
            ORDER BY B1, B2
        ) ID_B
        FROM A
    ) X LEFT JOIN B ON X.ID_B=B.ID_B

    SELECT A.*, X.B1, X.B2 FROM A LEFT JOIN (
        SELECT B.*, ROW_NUMBER() OVER (PARTITION BY ID_A ORDER BY B1,B2) N
        FROM B
    ) X ON A.ID_A=X.ID_A AND X.N=1;

    Răzvan
View as RSS news feed in XML
Powered by Community Server (Commercial Edition), by Telligent Systems