Domanda

Attualmente sto lavorando su un particolare complesso caso d'uso. Semplificare sotto:)

In primo luogo, un record cliente ha una relazione molti-a-uno con una collezione di servizi, vale a dire, un singolo client può avere più servizi ad esso associati.

Nel mio grilletto, sto scrivendo una query che restituisce ID di un cliente in base a determinati criteri. I criteri sono i seguenti,

  1. Se almeno un servizio è di tipo B, e non esistono servizi di tipo A, di ritorno id
  2. Se almeno un servizio è di tipo C, e non esistono servizi di tipo B o A, ritorno id
  3. Se almeno un servizio è di tipo D, e non servizi di tipo C o B o A esiste, restituire id

e il mio approccio attuale è quello di formare una query simile a quello qui sotto

SELECT c.ClientId
FROM
  Clients AS c
    -- actually INNER JOIN is superfluous in this sample, but required for
    -- other auxilliary criteria i have left out. illustrates relationship
    -- between Clients and Services table
    INNER JOIN Services AS s ON c.ClientId = s.ClientId
WHERE
-- has at least one service of type B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A'))) OR 

-- has at least one service of type C, no B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A'))) OR

-- has at least one service of type D, no C, no B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'D')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A')))

dove [dbo].[Get_ServicesByClientIdAndType] è una funzione che restituisce i servizi associati per id client specificato e il tipo di servizio. Simile a

-- this query is actually significantly more complex than shown
-- below, but this illustrates use of parameters client id and
-- service type
SELECT s.ServiceType
FROM
  Services AS s
WHERE
  s.ClientId = @clientId AND
  s.ServiceType = @serviceType

Supponendo che questo è ottimo mezzo per esprimere questo caso d'uso, funzionerebbe [dbo].[Get_ServicesByClientIdAndType] sub-query essere memorizzato nella cache o non modifica parametro di servizio necessaria una nuova valutazione ogni chiamata? [Sto invocando questa cosa come 9 volte !!! esegue SQL Server 2005]

Lo so SQL Server 2005 supporta alcune ottimizzazioni sub-query, come i risultati di caching, ma non so con certezza in quali circostanze e come la mia forma sub-query [o funzione] tale che faccio la maggior parte di Sql le capacità del server.


Modifica recensione miei criteri di cui sopra, e non poteva lasciare andare una fastidiosa sensazione qualcosa era spento. Ho suonato in giro con una certa logica nella mia testa, e si avvicinò con questo [molto più semplice] formulazione

SELECT c.ClientId
FROM
  Clients AS c
    INNER JOIN Services AS s ON c.ClientId = s.ClientId
WHERE
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A')) AND
    (EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) OR 
    EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) OR 
    EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'D')))

sostanzialmente, non esiste scenario coinvolge B che comporta il rigetto, in modo simile per C e D, quindi la configurazione è accettabile. abbiamo solo cura che A non è presente in qualsiasi selezione. Arg! Charlie Brown!


lasciando entrambe le espressioni per la revisione, e ho ancora molto apprezzare le risposte in materia di funzioni definite dall'utente WRT prestazioni di SQL Server.

È stato utile?

Soluzione

stavo scrivendo una risposta per la tua domanda e nel frattempo avete cambiato le vostre esigenze, ma non si dovrebbe avere alcun problema di convertire il mio soluzione ai vostri bisogni specifici ..

Ma vorrei iniziare fin dall'inizio. Sono abbastanza sicuro che SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A') non viene memorizzato nella cache in alcun modo dal server. Non è così intelligente;). Così si calcola più volte nella query principale

Così il vostro primo ottimizzazione dovrebbe andare in quella direzione. Si dovrebbe ridurre il numero di volte in cui viene chiamato Get_ServicesByClientIdAndType. Si può fare in molti modi. Ma la regola generale è che che si dovrebbe calcolare tutti i possibili risultati di questa funzione per tutti i tuoi clienti. Questi risultati dovrebbero essere messi in qualche tavolo temporarty o saranno puted in un virtuale whis tavolo è fatto da solo SQL Server.

Quando avete ottenuto il vostro tutti i risultati possibili semplicemente unirsi a loro con la vostra tabella di clienti. Ma loro si uniscono solo UNA VOLTA .

Naturalmente molte cose e l'ottimizzazione trucco dipende dal vostro esempio reale. Nell'esempio che hai dato non v'è ancora alcuna necessità di utilizzo Get_ServicesByClientIdAndType. Perché non semplicemente unirsi a quei due tabelle ed eseguire alcuni calcoli su di loro?

Date un'occhiata a questa domanda:

SELECT A.* FROM
(
 SELECT C.ClientID,
  SUM(CASE(S.ServiceType) WHEN 'A' THEN 1 ELSE 0 END) AS ServiceA,
  SUM(CASE(S.ServiceType) WHEN 'B' THEN 1 ELSE 0 END) AS ServiceB,
  SUM(CASE(S.ServiceType) WHEN 'C' THEN 1 ELSE 0 END) AS ServiceC,
  SUM(CASE(S.ServiceType) WHEN 'D' THEN 1 ELSE 0 END) AS ServiceD
 FROM Clients AS C
 INNER JOIN Services AS s ON c.ClientId = s.ClientId
 GROUP BY C.ClientID
) A
WHERE ((A.ServiceB > 0) AND (A.ServiceA = 0)) 
 OR ((A.ServiceC > 0) AND (A.ServiceA = 0) AND (A.ServiceB = 0))
 OR ((A.ServiceD > 0) AND (A.ServiceA = 0) AND (A.ServiceB = 0) AND (A.ServiceC = 0))

Nella query interna che unire le tabelle. Gettiamo via la funzione in quanto noi non averne bisogno. Invece, si calcola il numero di servizi diversi per ogni cliente. Successivo sopra i risultati della query interne realizziamo le vostre condizioni. Abbiamo semplicemente di controllo per il verificarsi di determinati servizi in un set particual.

Il risultato è simile a questo:

ClientID ServiceA ServiceB ServiceC ServiceD
-------- -------- -------- -------- --------
26915       0        4        2        2
26917       0        0        1        1
26921       0        3        2        3
26927       0        4        2        4

Naturalmente si può spogliare il risultato finale dalle colonne di servizio. Ho incluso loro perché mi piace quel modo ;-) e consente di verificare se la query funziona correttamente. È anche possibile scrivere una query che non calcolare il numero di dato tipo di servizio per un determinato cliente. Si lavorerà ancora più veloce e vi darà i risultati corretti.

Anche se si ha realmente bisogno la funzione, perché non cambiare la sua attuazione in modo che la funzione ritornerà e ID dopo la prima di successo unirsi? Ti farà risparmiare un sacco di tempo.

Ma solo si conosce il quadro più ampio in modo tutto quello che ho scritto qui potrebbe essere spazzatura; -)

In ogni caso, spero che ho aiutato in qualche modo.

Altri suggerimenti

Direi che di SQL Server chiama la funzione Get_ServicesByClientIdAndType una volta per ogni combinazione di valori dei parametri, ma che per ogni riga della tabella Clienti. Avete tre combinazioni di valori, quindi per 100 righe nella tabella client si potrebbe vedere 300 chiamate della funzione.

Ma per essere sicuri, eseguire la query in studio la gestione di SQL Server e attivare l'opzione "piano di spettacolo di esecuzione". In questo modo si può facilmente rilevare che una parte della query consuma più risorse e conentrate sull'ottimizzazione quella parte.

Una cosa da tenere a mente è quello di evitare "NOT", se possibile. "NOT" non è sargable, non sarà in grado di prendere i vantaggi completi di indicizzazione. A prima vista, non vedo un modo per riscrivere per evitare le espressioni NON però. FWIW, YMMV. : -)

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