Posso fare una partita della prima lettera di grandi dimensioni su due tavoli?


9
select value 
from persons p join persons2 p2 
    on left(p.lastname,1) = left(p2.lastname,1)

Server SQL. C'è un modo per rendere questo SARGable / correre più veloce? Non riesco a creare colonne sul tavolo people, ma posso creare colonne su people2.


3
Sai che il risultato di quella query sarà una specie di CROSS JOIN, in realtà?
ypercubeᵀᴹ

1
Quanto sono grandi i tavoli? Se ciascuno di essi dice solo 10K righe, il risultato sarà almeno 4 milioni di righe. Mi chiedo quale sarà l'uso di tale query.
ypercubeᵀᴹ

1
@ ypercubeᵀᴹ forse un input iniziale in qualche processo di deduplicazione usando la corrispondenza fuzzy?
Martin Smith,

Sembra una cattiva idea. Cosa stai cercando di ottenere qui?
David Markודו Markovitz

Questo era solo per esempio. Ci sono più predicati. Martin Smith ha l'idea giusta, è per la deduplicazione.
lastchancexi,

Risposte:


9

Creare una vista sulle tabelle con una colonna calcolata persistente definita come LEFT(lastname, 1) di ciascuna tabella, quindi confrontare i valori della colonna persistente calcolata.

Ecco un banco di prova che mostra come farlo:

CREATE TABLE dbo.Persons
(
    PersonID int NOT NULL
        CONSTRAINT PK_Persons
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , FirstName nvarchar(500) NOT NULL
    , LastName nvarchar(500) NOT NULL
);

CREATE TABLE dbo.Persons2
(
    PersonID int NOT NULL
        CONSTRAINT PK_Persons2
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , FirstName nvarchar(500) NOT NULL
    , LastName nvarchar(500) NOT NULL
);

GO
CREATE VIEW dbo.PersonsView
WITH SCHEMABINDING
AS
SELECT p1.PersonID
    , p1.FirstName
    , p1.LastName 
    , LastNameInitial = LEFT(p1.LastName, 1)
FROM dbo.Persons p1;
GO
CREATE VIEW dbo.PersonsView2
WITH SCHEMABINDING
AS
SELECT p2.PersonID
    , p2.FirstName
    , p2.LastName 
    , LastNameInitial = LEFT(p2.LastName, 1)
FROM dbo.Persons p2;
GO
CREATE UNIQUE CLUSTERED INDEX CX_PersonsView
ON dbo.PersonsView(PersonID);
CREATE NONCLUSTERED INDEX IX_PersonsView_LastNameInitial
ON dbo.PersonsView(LastNameInitial)
INCLUDE (FirstName, LastName);

CREATE UNIQUE CLUSTERED INDEX CX_PersonsView2
ON dbo.PersonsView2(PersonID);
CREATE NONCLUSTERED INDEX IX_PersonsView2_LastNameInitial
ON dbo.PersonsView2(LastNameInitial)
INCLUDE (FirstName, LastName);

CREATE STATISTICS ST_PersonsView_001
ON dbo.PersonsView(LastName);

CREATE STATISTICS ST_PersonsView2_001
ON dbo.PersonsView2(LastName);

Qui inseriremo alcuni dati di esempio:

INSERT INTO dbo.Persons(FirstName, LastName)
VALUES ('Max', 'Vernon')
    , ('Joe', 'Black');

INSERT INTO dbo.Persons2(FirstName, LastName)
VALUES ('Max', 'Vernon')
    , ('Joe', 'Black');

Ecco la SELECTdomanda:

SELECT *
FROM dbo.PersonsView pv1
    INNER JOIN dbo.PersonsView2 pv2 ON pv1.LastNameInitial = pv2.LastNameInitial;

E i risultati:

+ ---------- + ----------- + ---------- + --------------- - + ---------- + ----------- + ---------- + ------------- ---- +
| PersonID | Nome | Cognome | LastNameInitial | PersonID | Nome | Cognome | LastNameInitial |
+ ---------- + ----------- + ---------- + --------------- - + ---------- + ----------- + ---------- + ------------- ---- +
| 2 | Joe | Nero | B | 2 | Joe | Nero | B |
| 1 | Max | Vernon | V | 1 | Max | Vernon | V |
+ ---------- + ----------- + ---------- + --------------- - + ---------- + ----------- + ---------- + ------------- ---- +

Il piano di esecuzione, con solo due righe per tabella (certamente non molte righe!)

inserisci qui la descrizione dell'immagine


11

Se la lastnamecolonna è indicizzata in almeno una delle tabelle, è possibile utilizzare ancheLIKE

SELECT *
FROM   persons p
       INNER JOIN persons2 p2
               ON p2.lastname LIKE LEFT(p.lastname, 1) + '%' 

inserisci qui la descrizione dell'immagine

Il piano per questo può avere una ricerca sulla tabella specificata a sinistra del simile.

cioè ON p.lastname LIKE LEFT(p2.lastname, 1) + '%'non sarebbe in grado di utilizzare l'indice su persons2quello che è stato usato sopra ma potrebbe cercarne uno persons.

Il suggerimento nell'altra risposta di indicizzare una colonna calcolata su entrambi i lati è tuttavia più flessibile. Per quanto riguarda un piano di cicli nidificati, una delle tabelle può trovarsi all'interno e consentirebbe anche l'unione di molti o molti senza richiedere un ordinamento.


che dire di questo approccio ? Sentiti libero di aggiungerlo nella tua risposta se ha qualche vantaggio. Utilizzerebbe gli indici su entrambe le tabelle e, in tal caso, sarebbe più efficiente?
ypercubeᵀᴹ

@ ypercubeᵀᴹ Potrebbe dare un piano come questo se gli indici coprano i.stack.imgur.com/RSzcT.png . Tuttavia, non vedo alcun vantaggio rispetto al piano nella mia risposta. Dato che alla fine sarà ancora necessario leggere tutte le righe nella tabella esterna, proprio ora tramite 26 cerca anziché una scansione.
Martin Smith,

2

Mi capita di avere una tabella con 3.423 righe e 195 valori distinti in Name. Chiamerò questa tabella P(persona) e la duplicherò per creare P2(persona2). Esiste una chiave primaria univoca in cluster su una colonna ID intero. Sto utilizzando Microsoft SQL Server 2016 (KB3194716) Developer Edition (64 bit) su Windows 10 Pro 6.3 con 32 GB di RAM.

Con la query di base

select
    p.pid
from dbo.p
inner join dbo.p2 
    on LEFT(p.name, 1) = LEFT(p2.name, 1);

Ottengo 1.5M righe restituite in 3200-3300ms (dalle statistiche io).

inserisci qui la descrizione dell'immagine

Riscrivendo così -

select
    p.pid
from dbo.p
where exists
(
    select 1
    from dbo.p2 
    where LEFT(p.name, 1) = LEFT(p2.name, 1)
);

trascorso è ridotto a 50-60 ms e il piano è:

inserisci qui la descrizione dell'immagine

Vengono restituite meno righe (3.423) a causa dell'algoritmo corrispondente. Lo stesso piano e conteggio righe vengono raggiunti modificando la query di base in select distinct.

Creando una colonna indicizzata e calcolata

alter table dbo.p2
add Name1 as Left(Name, 1);

create index ix1 on dbo.p2(Name1);

Il tempo trascorso scende a 45-50ms.

inserisci qui la descrizione dell'immagine

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.