2016-03-26 40 views
6

Ho una query simile in natura di seguito in una stored procedure che dispone di un singolo parametro:filtraggio condizionale aderire

SELECT 
    ID, 
    DepartmentID, 
    FileName 
FROM 
    Document 
-- conditional join from here 
JOIN 
    AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID 
          AND @IsAdmin = 'false' 

Il parametro è @IsAdmin con il bit di tipo di dati.

Le due tabelle con cui lavoro sono la tabella Document (vedere la struttura nella query sopra) e la AllowedDepartmentList che contiene una singola colonna int.

Uso questa query per filtrare i risultati restituiti della tabella Document con join. Non uso la clausola WHERE DepartmentID IN(), perché la ListaDiDelegato può essere lunga fino a 600-700 elementi (troppa per IN() da gestire con buone prestazioni in una tabella di record potenzialmente 1M) Quindi filtro usando un join, ma il filtro deve essere solo eseguire, se il parametro @IsAdmin è false. Come le righe dopo il - join condizionale da qui il commento non era nemmeno lì.

Ho provato la query sopra ma non produce alcun record. Sospetto che sto usando il tipo sbagliato di join, ma sono bloccato.

risposta

3

È possibile utilizzare un LEFT JOIN in combinazione di un dove che o richiede privilegi di amministratore, o una partita sul unirsi

SELECT 
    ID, 
    DepartmentID, 
    FileName 
FROM 
    Document 
-- conditional join from here 
LEFT JOIN 
    AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID 
WHERE 
    @IsAdmin = 'true' OR AllowedDepartmentList.ID IS NOT NULL 
+1

Nota che il suggerimento di jhilden è più efficiente, dal momento che ti salverai da un join nelle situazioni in cui non è necessario. Tuttavia, comprendo la tua argomentazione su come mantenere una sola query. –

+0

Grazie mille, questo sembra essere OK, devo provarlo. E sì, hai ragione, dobbiamo anche verificare se i join in più causano un sovraccarico tale che vale la pena duplicare il codice. – Daniel

+0

Ho accettato la risposta poiché abbiamo implementato questa soluzione a scopo di test. Funziona come necessario e svolge il lavoro, ma le prestazioni devono essere misurate. Ci sono molte altre risposte che si adattano al conto, quindi grazie per tutti coloro che hanno avuto il tempo di giocare con il mio problema. – Daniel

3

Come regola generale, inserire i parametri in WHERE clausole VS join. Una soluzione alquanto semplice sarebbe avere un TVF o sproc che esegua due query completamente differenti. qualcosa di simile:

IF (@isAdmin = 0) --notice I used a SQL bool vs a string of 'false' 
BEGIN 
    SELECT 
     ID, 
     DepartmentID, 
     FileName 
    FROM 
     Document 
    JOIN 
     AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID; 
END; 
ELSE 
    [email protected] is not false, so don't join 
    SELECT 
     ID, 
     DepartmentID, 
     FileName 
    FROM 
     Document; 
END; 
+0

Questo è quello che ho cercato di evitare: duplicare la selezione. La selezione esatta nel nostro SP è molto più complicata (come 150 righe) e diventa molto brutta se l'intero shebang è duplicato ... per non parlare delle modifiche future. – Daniel

+0

@Daniel Se l'intero shebang è di 150 righe, è un motivo in più per avere una query efficiente. Quanto può essere difficile una copia incolla e rimuovere un join? – Paparazzi

+0

Hai assolutamente ragione in questa materia. Dovremo fare un test di stress su dati reali per capire la risposta migliore. Stavo solo cercando un'alternativa alla duplicazione del codice, ma non mi sarei mai aspettato di ricevere tanti buoni suggerimenti (incluso rimanere sulla traccia corrente, proprio come jhilden - e tu - suggerivi). – Daniel

2

La seguente query potrebbe aiutare

SELECT ID 
     ,DepartmentID 
     ,FileName 
FROM Document 
     LEFT JOIN AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID 
WHERE @isAdmin = 1 
    OR (@isAdmin = 0 AND AllowedDepartmentList.ID IS NOT NULL) 
3

è possibile utilizzare esiste condizione, in questo modo:

SELECT 
ID, 
DepartmentID, 
FileName 
FROM Document 
WHERE exists(
SELECT 1 from AllowedDepartmentList 
WHERE DepartmentID = AllowedDepartmentList.ID) 
OR @IsAdmin = 'true' 
+0

Come si confronta il rendimento con JOIN? – Daniel

+1

Penso che sia più veloce, specialmente se usi condizioni aggiuntive, ma devi provare. partecipare è ottenere dati, l'esistenza è perfetta per te :) –

+0

Unire può causare la moltiplicazione dei record, non esiste. –

3

Un altro modo per farlo utilizzando Dynamic query. Questo avrà buone prestazioni rispetto alle opzioni JOIN/Exists

DECLARE @sql  NVARCHAR(max)='', 
     @IsAdmin BIT = 1 

SET @sql = ' 
SELECT 
    ID, 
    DepartmentID, 
    FileName 
FROM 
    Document D 
    ' + CASE WHEN @IsAdmin = 'false' THEN ' where exists (select 1 from AllowedDepartmentList AD where D.DepartmentID = AD.ID) ' ELSE '' END 

--PRINT @sql 

exec sp_executesql @sql 

di query in modo dinamico incorniciato quando @IsAdmin = 0

SELECT ID, 
     DepartmentID, 
     FileName 
FROM Document D 
WHERE EXISTS (SELECT 1 
       FROM AllowedDepartmentList AD 
       WHERE D.DepartmentID = AD.ID) 

di query in modo dinamico incorniciato quando @IsAdmin = 0

SELECT ID, 
     DepartmentID, 
     FileName 
FROM Document D 
1

aggiungere un AllowedDepartmentList.ID 'come -1 per l'amministratore
passare un valore null o -1 per @admin ID

ON isnull(@adminID, DepartmentID) = AllowedDepartmentList.ID 

un OR (o @IsAdmin = 'true') non è efficiente e anche questo solo restituisce 1

+0

Questo ucciderà qualsiasi indice presente su 'DepartmentID' –

+0

@Prdp Quindi, presumo AllowedDepartmentList.ID è un PK. Non è peggio di un ESISTE con meno codice. – Paparazzi