Domanda

Ho un caso in cui ho bisogno di tradurre (ricerca) più valori dalla stessa tabella. Il primo modo in cui ho scritto, è stato utilizzando sottoquery:

SELECT
    (SELECT id FROM user WHERE user_pk = created_by) AS creator,
    (SELECT id FROM user WHERE user_pk = updated_by) AS updater,
    (SELECT id FROM user WHERE user_pk = owned_by) AS owner,
    [name]
FROM asset

Come io sto usando questo subquery un sacco (che è, ho circa 50 tavoli con questi campi), e potrei avere bisogno di aggiungere un po 'di codice per la sottoquery (ad esempio, "e attivo = 1") I ho pensato di mettere queste in una funzione UDF definita dall'utente e usare quella. Ma le prestazioni usando che UDF era spaventoso.

CREATE FUNCTION dbo.get_user ( @user_pk INT )
RETURNS INT
AS BEGIN 
    RETURN ( SELECT id
             FROM   ice.dbo.[user]
             WHERE  user_pk = @user_pk )
END

SELECT dbo.get_user(created_by) as creator, [name]
FROM asset

Le prestazioni del 1 # è meno di 1 secondo. Prestazioni di # 2 è di circa 30 secondi ...

Perché, o più importante, c'è un modo posso codice in SQL Server 2008, in modo che non devo usare tante subquery?

Modifica:

Basta un pò di più spiegazione di quando questo è utile. Questa semplice query (cioè, ottenere userid) diventa molto più complesso quando voglio avere un testo per un utente, dal momento che ho a unirsi con profilo per ottenere la lingua, con una società per vedere se la lingua dovrebbe essere recuperare' ed invece da lì, e con la tabella di conversione per ottenere il testo tradotto. E per la maggior parte di queste domande, la prestazione è un problema secondario per la leggibilità e la manutenibilità.

È stato utile?

Soluzione

L'UDF è una scatola nera per l'ottimizzatore di query in modo che sia eseguito per ogni riga. State facendo un cursore riga per riga. Per ogni riga in un bene, cercare un id tre volte in un altro tavolo. Questo succede quando si utilizza scalari o più istruzioni UDF (in linea UDF sono semplicemente le macro che si espandono nella query esterna)

Uno dei molti articoli sul problema è " scalare funzioni, inline, e le prestazioni:. un titolo divertente per un posto noioso "

I sub-query possono essere ottimizzati per correlare ed evitare le operazioni di riga per riga.

Che cosa si vuole veramente è questo:

SELECT
   uc.id AS creator,
   uu.id AS updater,
   uo.id AS owner,
   a.[name]
FROM
    asset a
    JOIN
    user uc ON uc.user_pk = a.created_by
    JOIN
    user uu ON uu.user_pk = a.updated_by
    JOIN
    user uo ON uo.user_pk = a.owned_by

Aggiornamento Feb 2019

2019 inizia SQL Server per risolvere questo problema.

Altri suggerimenti

Come altri utenti hanno suggerito, usando unisce sicuramente vi darà le migliori prestazioni complessive.

Tuttavia, dal momento che hai dichiarato che che non si desidera che il mal di testa di mantenere 50-ish simile join o sottoquery, provare a utilizzare una funzione con valori di tabella in linea come segue:

CREATE FUNCTION dbo.get_user_inline (@user_pk INT)
RETURNS TABLE AS
RETURN
(
    SELECT TOP 1 id
    FROM ice.dbo.[user]
    WHERE user_pk = @user_pk
        -- AND active = 1
)

La vostra query originale sarebbe poi diventato qualcosa di simile:

SELECT
    (SELECT TOP 1 id FROM dbo.get_user_inline(created_by)) AS creator,
    (SELECT TOP 1 id FROM dbo.get_user_inline(updated_by)) AS updater,
    (SELECT TOP 1 id FROM dbo.get_user_inline(owned_by)) AS owner,
    [name]
FROM asset

in linea con valori di tabella funzioni dovrebbe avere prestazioni migliori rispetto sia una funzione scalare o una tabella con valori di istruzioni multiple funzioni.

Le prestazioni dovrebbero essere più o meno equivalente alla query originale, ma eventuali modifiche possono essere fatte in UDF, rendendo molto più gestibile.

Per ottenere lo stesso risultato (NULL se l'utente viene eliminato o non attivo).

 select 
    u1.id as creator,
    u2.id as updater,
    u3.id as owner,
    [a.name]
 FROM asset a
        LEFT JOIN user u1 ON (u1.user_pk = a.created_by AND u1.active=1) 
        LEFT JOIN user u2 ON (u2.user_pk = a.created_by AND u2.active=1) 
        LEFT JOIN user u3 ON (u3.user_pk = a.created_by AND u3.active=1) 

Mi sto perdendo qualcosa? Perché non è possibile questo lavoro? Si sta selezionando solo l'ID che hai già nella tabella:

select created_by as creator, updated_by as updater, 
owned_by as owner, [name]
from asset

Tra l'altro, nella progettazione si dovrebbe davvero evitare di parole chiave, come name, come nomi di campo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top