Primo 1 con un join a sinistra


92

Data la query di seguito, potrebbero esserci più righe in dps_markers con la stessa chiave di marcatore, ma vogliamo solo unirle alla prima. Se prendo questa query e rimuovo il primo 1 e ORDER BY ottengo un valore per mbg.marker_value ma eseguito così com'è, restituisce sempre null

SELECT u.id, mbg.marker_value 
FROM dps_user u
LEFT JOIN 
    (SELECT TOP 1 m.marker_value, um.profile_id
     FROM dps_usr_markers um (NOLOCK)
         INNER JOIN dps_markers m (NOLOCK) 
             ON m.marker_id= um.marker_id AND 
                m.marker_key = 'moneyBackGuaranteeLength'
     ORDER BY m.creation_date
    ) MBG ON MBG.profile_id=u.id 
WHERE u.id = 'u162231993'

Risposte:


196

Usa OUTER APPLY invece di LEFT JOIN:

SELECT u.id, mbg.marker_value 
FROM dps_user u
OUTER APPLY 
    (SELECT TOP 1 m.marker_value, um.profile_id
     FROM dps_usr_markers um (NOLOCK)
         INNER JOIN dps_markers m (NOLOCK) 
             ON m.marker_id= um.marker_id AND 
                m.marker_key = 'moneyBackGuaranteeLength'
     WHERE um.profile_id=u.id 
     ORDER BY m.creation_date
    ) AS MBG
WHERE u.id = 'u162231993';

A differenza di JOIN, APPLY ti consente di fare riferimento all'u.id all'interno della query interna.


Grazie @ Remus, mi ha aiutato.
Sarthak Shah

2

La chiave per eseguire il debug di situazioni come queste è eseguire la subquery / vista inline da sola per vedere qual è l'output:

  SELECT TOP 1 
         dm.marker_value, 
         dum.profile_id
    FROM DPS_USR_MARKERS dum (NOLOCK)
    JOIN DPS_MARKERS dm (NOLOCK) ON dm.marker_id= dum.marker_id 
                                AND dm.marker_key = 'moneyBackGuaranteeLength'
ORDER BY dm.creation_date

Eseguendolo, vedresti che il profile_idvalore non corrisponde al u.idvalore di u162231993, il che spiegherebbe il motivo per cui tutti i mbgriferimenti sarebbero tornatinull (grazie al join sinistro; non otterrai nulla se fosse un join interno).

Ti sei codificato in un angolo usando TOP, perché ora devi modificare la query se vuoi eseguirla per altri utenti. Un approccio migliore sarebbe:

   SELECT u.id, 
          x.marker_value 
     FROM DPS_USER u
LEFT JOIN (SELECT dum.profile_id,
                  dm.marker_value,
                  dm.creation_date
             FROM DPS_USR_MARKERS dum (NOLOCK)
             JOIN DPS_MARKERS dm (NOLOCK) ON dm.marker_id= dum.marker_id 
                                         AND dm.marker_key = 'moneyBackGuaranteeLength'
           ) x ON x.profile_id = u.id
     JOIN (SELECT dum.profile_id,
                  MAX(dm.creation_date) 'max_create_date'
             FROM DPS_USR_MARKERS dum (NOLOCK)
             JOIN DPS_MARKERS dm (NOLOCK) ON dm.marker_id= dum.marker_id 
                                         AND dm.marker_key = 'moneyBackGuaranteeLength'
         GROUP BY dum.profile_id) y ON y.profile_id = x.profile_id
                                   AND y.max_create_date = x.creation_date
    WHERE u.id = 'u162231993'

Con ciò, puoi modificare il idvalore nella whereclausola per controllare i record per qualsiasi utente nel sistema.


1

Perché il TOP 1dalla sottoquery ordinata non ha profile_id = 'u162231993' Rimuoviwhere u.id = 'u162231993' e quindi vedi i risultati.

Esegui la sottoquery separatamente per capire cosa sta succedendo.


ok penso di capire cosa intendi ora. deve ancora essere in grado di farlo funzionare. Fondamentalmente, la tabella dps_markers potrebbe avere più di una riga che causa duplicazioni nella query esterna che dobbiamo prevenire.
dstarh

0

Damir ha ragione,

La tua sottoquery deve assicurarsi che dps_user.id sia uguale a um.profile_id, altrimenti prenderà la prima riga che potrebbe, ma probabilmente non essere uguale al tuo id di "u162231993"

La tua query dovrebbe essere simile a questa:

SELECT u.id, mbg.marker_value 
FROM dps_user u
LEFT JOIN 
    (SELECT TOP 1 m.marker_value, um.profile_id
     FROM dps_usr_markers um (NOLOCK)
         INNER JOIN dps_markers m (NOLOCK) 
             ON m.marker_id= um.marker_id AND 
                m.marker_key = 'moneyBackGuaranteeLength'
     WHERE u.id = um.profile_id
     ORDER BY m.creation_date
    ) MBG ON MBG.profile_id=u.id 
WHERE u.id = 'u162231993'

sì, l'ho appena provato, ma u.id non è visibile nella sottoselezione Impossibile associare l'identificatore multiparte "u.id".
dstarh

Potresti inserire WHERE um.profile_id = 'u162231993'la sottoquery e WHERE mbg.marker_value IS NOT NULLall'esterno.
Damir Sudarevic

Non conoscerò il profile_id, sarà da un altro join. Questo è stato estratto da una query molto più ampia
dstarh

bene, usa la variabile @SearchFor = 'u162231993'e poi usala in WHERE, o pubblica alcuni dati e strutture di tabelle in modo che altre persone possano aiutare e provarlo.
Damir Sudarevic
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.