Ho una vista sql, che sto usando per recuperare i dati. Diciamo che è un grande elenco di prodotti, che sono collegati ai clienti che li hanno acquistati. La vista dovrebbe restituire solo una riga per prodotto, indipendentemente dal numero di clienti a cui è collegato. Sto usando la funzione row_number per raggiungere questo obiettivo. (Questo esempio è semplificato, la situazione generica sarebbe una query in cui ci dovrebbe essere solo una riga restituita per ogni valore unico di X. colonna Quale riga viene restituita non è importante)Rifattorizzazione di una vista tsql che utilizza row_number() per restituire righe con un valore di colonna univoco
CREATE VIEW productView AS
SELECT * FROM
(SELECT
Row_number() OVER(PARTITION BY products.Id ORDER BY products.Id) AS product_numbering,
customer.Id
//various other columns
FROM products
LEFT OUTER JOIN customer ON customer.productId = prodcut.Id
//various other joins
) as temp
WHERE temp.prodcut_numbering = 1
Ora consente di dire che la il numero totale di righe in questa vista è ~ 1 milione e la selezione in esecuzione * da productView richiede 10 secondi. Esecuzione di una query come selezionare * da productView dove productID = 10 richiede lo stesso tempo. Credo che questo sia perché la query viene valutata a questo
SELECT * FROM
(SELECT
Row_number() OVER(PARTITION BY products.Id ORDER BY products.Id) AS product_numbering,
customer.Id
//various other columns
FROM products
LEFT OUTER JOIN customer ON customer.productId = prodcut.Id
//various other joins
) as temp
WHERE prodcut_numbering = 1 and prodcut.Id = 10
Penso che questa è la causa del subquery interna da valutare in pieno ogni volta. Idealmente mi piacerebbe usare qualcosa lungo le seguenti linee
SELECT
Row_number() OVER(PARTITION BY products.productID ORDER BY products.productID) AS product_numbering,
customer.id
//various other columns
FROM products
LEFT OUTER JOIN customer ON customer.productId = prodcut.Id
//various other joins
WHERE prodcut_numbering = 1
Ma questo non sembra essere consentito. C'è un modo per fare qualcosa di simile?
EDIT -
Dopo molti esperimenti, il vero problema credo che sto avendo è come forzare un join per tornare esattamente 1 fila. Ho provato ad usare external apply, come suggerito di seguito. Alcuni esempi di codice.
CREATE TABLE Products (id int not null PRIMARY KEY)
CREATE TABLE Customers (
id int not null PRIMARY KEY,
productId int not null,
value varchar(20) NOT NULL)
declare @count int = 1
while @count <= 150000
begin
insert into Customers (id, productID, value)
values (@count,@count/2, 'Value ' + cast(@count/2 as varchar))
insert into Products (id)
values (@count)
SET @count = @count + 1
end
CREATE NONCLUSTERED INDEX productId ON Customers (productID ASC)
Con il set di esempio sopra riportato, la query 'avere tutto' sotto
select * from Products
outer apply (select top 1 *
from Customers
where Products.id = Customers.productID) Customers
prende ~ 1000 ms per l'esecuzione. Aggiunta di una condizione esplicita:
select * from Products
outer apply (select top 1 *
from Customers
where Products.id = Customers.productID) Customers
where Customers.value = 'Value 45872'
Richiede una quantità identica di tempo. Questo 1000 ms per una query abbastanza semplice è già troppo e ridimensiona il modo sbagliato (verso l'alto) quando si aggiungono altri join simili.
Avete bisogno effettivi dati dei clienti o solo esistenza di o solo un CustomerID? La sottoquery è valutata perché il "10" non è noto in anticipo. E stai chiedendo esattamente la decima fila. Da qui la mia prima domanda sull'output desiderato – gbn
Osservazione veramente buona - SQL non è in grado di applicare il filtro di visualizzazione nella sottoquery. Hai davvero bisogno della flessibilità della vista? Se hai usato una funzione SPROC o una tabella con valori con filtri definiti (ProductID nel tuo esempio), potresti creare il filtro nella subquery. E nel caso in cui la tua PARTITION BY e il FILTER siano gli stessi (ProductId), non avrai affatto bisogno di PARTITION - quindi SELECT TOP 1 dovrebbe essere sufficiente. – StuartLC
Ho bisogno dei dettagli del cliente (o valori nulli se nessuno esiste), non solo dell'esistenza di uno. Devo anche usare una vista, refactoring dell'app che recupera i dati non è possibile. – John