seleziona * vs seleziona colonna


124

Se ho solo bisogno di 2/3 colonne e interrogo SELECT *invece di fornire quelle colonne nella query di selezione, c'è qualche degrado delle prestazioni per quanto riguarda più / meno I / O o memoria?

L'overhead di rete potrebbe essere presente se seleziono * senza necessità.

Ma in un'operazione di selezione, il motore di database estrae sempre la tupla atomica dal disco o estrae solo le colonne richieste nell'operazione di selezione?

Se estrae sempre una tupla, l'overhead di I / O è lo stesso.

Allo stesso tempo, potrebbe esserci un consumo di memoria per l'eliminazione delle colonne richieste dalla tupla, se estrae una tupla.

Quindi, se questo è il caso, seleziona someColumn avrà più overhead di memoria di quello di select *


C'è un RDBMS specifico di cui stai chiedendo? È possibile che il modo in cui le SELECTquery vengono eseguite / elaborate sia diverso da database a database.
Lèse majesté

10
Per inciso, in PostgreSQL, se dici CREATE VIEW foo_view AS SELECT * FROM foo;, quindi aggiungi colonne alla tabella foo in seguito, quelle colonne non verranno visualizzate automaticamente in foo_view come previsto. In altre parole, *in questo contesto si espande solo una volta (al momento della creazione della vista), non per SELECT. A causa delle complicazioni derivanti da ALTER TABLE, direi che (in pratica) *è considerato dannoso.
Joey Adams

@JoeyAdams - non solo PostgresQL, questo è anche il comportamento di Oracle.
APC

1
@OMG Ponies: non ero a conoscenza di post simili. Tuttavia, questi non sono realmente similari. @ Lèse majesté: sto parlando di RDBMS generico. non su un venditore specifico @ Joey Adams: Hmm lo so che * non è sicuro. voglio solo discutere i problemi di prestazioni riguardanti.
Neel Basu

Risposte:


31

Estrae sempre una tupla (tranne nei casi in cui la tabella è stata segmentata verticalmente, suddivisa in parti di colonne), quindi, per rispondere alla domanda che hai posto, non importa dal punto di vista delle prestazioni. Tuttavia, per molti altri motivi, (sotto) dovresti sempre selezionare in modo specifico le colonne che desideri, per nome.

Estrae sempre una tupla, perché (in tutti i fornitori RDBMS con cui ho familiarità), la struttura di archiviazione su disco sottostante per tutto (inclusi i dati della tabella) è basata su pagine di I / O definite (in SQL Server, ad esempio, ogni pagina è 8 kilobyte). E ogni lettura o scrittura di I / O è per Pagina .. Cioè, ogni scrittura o lettura è una Pagina completa di dati.

A causa di questo vincolo strutturale sottostante, una conseguenza è che ogni riga di dati in un database deve essere sempre su una e una sola pagina. Non può estendersi su più pagine di dati (ad eccezione di cose speciali come i BLOB, in cui i dati effettivi del BLOB sono archiviati in blocchi di pagina separati e la colonna della riga della tabella effettiva riceve solo un puntatore ...). Ma queste eccezioni sono solo questo, eccezioni e generalmente non si applicano tranne in casi speciali (per tipi speciali di dati o determinate ottimizzazioni per circostanze speciali)
Anche in questi casi speciali, generalmente, la riga di dati della tabella stessa (che contiene il puntatore ai dati effettivi per il BLOB, o qualsiasi altra cosa), deve essere archiviato su una singola pagina IO ...

ECCEZIONE. L'unico posto in cui Select *è OK, è nella sottoquery dopo una clausola Existso Not Existspredicate, come in:

   Select colA, colB
   From table1 t1
   Where Exists (Select * From Table2
                 Where column = t1.colA)

EDIT: Per affrontare il commento di @Mike Sherer, Sì, è vero, sia tecnicamente, con un po 'di definizione per il tuo caso speciale, sia esteticamente. Innanzitutto, anche quando l'insieme di colonne richieste è un sottoinsieme di quelle memorizzate in un indice, Query Processor deve recuperare ogni colonna memorizzata in quell'indice, non solo quelle richieste, per gli stessi motivi - TUTTO I / O deve essere eseguito in pagine e i dati dell'indice vengono archiviati nelle pagine IO proprio come i dati della tabella. Quindi, se definisci "tupla" per una pagina di indice come l'insieme di colonne memorizzato nell'indice, l'istruzione è ancora vera.
e l'affermazione è vera esteticamente perché il punto è che recupera i dati in base a ciò che è memorizzato nella pagina I / O, non a ciò che chiedi, e questo è vero sia che tu stia accedendo alla pagina I / O della tabella di base o a un indice Pagina I / O.

Per altri motivi per non utilizzarlo Select *, vedere Perché è SELECT *considerato dannoso? :


"Tira sempre una tupla" sei sicuro? Hmm Okay Quindi avevo ragione. in tal caso, select *si avrà un sovraccarico di memoria inferiore select columnma lo stesso overhead di I / O. quindi se lasciamo il sovraccarico di rete. select *se meno sovraccarico di quello diselect column
Neel Basu

10
Questo non è vero. Un esempio fuori dalla mia testa è quando vuoi solo il valore di una colonna indicizzata in MySQL (ad esempio, solo per verificare l'esistenza di righe) e stai usando il motore di archiviazione MyISAM, prenderà i dati dal File MYI, che potrebbe essere in memoria e nemmeno andare su disco!
Mike Sherov

Sì, se il set di tupla richiesto è in memoria non ci sarà I / O ma questo è un caso speciale. Allora, qual è l'estate. Se seleziono una colonna indicizzata, l'intera tupla non viene letta? altrimenti l'intera tupla viene letta?
Neel Basu

Non sono esattamente sicuro di come MySql esegua la memorizzazione nella cache, ma in SQL Server e in Oracle, anche quando i dati sono nella cache in memoria, accede comunque utilizzando la stessa struttura della pagina come farebbe quando si accede dal disco. il che significa che richiederebbe un I / O di memoria per pagina di dati ... esattamente lo stesso che dal disco. (tranne che gli I / O della memoria sono molto più veloci degli I / O del disco ovviamente). In effetti, questo è l'obiettivo della progettazione della cache, per rendere il processo di accesso totalmente indipendente dalla posizione dei dati.
Charles Bretana

2
Puoi precisare di più il "per molti altri motivi"? Perché quelli non mi erano chiari. Se le prestazioni non contano, perché preoccuparsi di richiedere i nomi delle colonne?
Dennis

111

Ci sono diversi motivi per cui non dovresti mai (mai e poi mai) usare SELECT *nel codice di produzione:

  • poiché non stai dando al tuo database alcun suggerimento su ciò che desideri, sarà prima necessario controllare la definizione della tabella per determinare le colonne su quella tabella. Quella ricerca costerà del tempo, non molto in una singola query, ma si somma nel tempo

  • se hai bisogno solo di 2/3 delle colonne, stai selezionando 1/3 di dati di troppo che devono essere recuperati dal disco e inviati attraverso la rete

  • se inizi a fare affidamento su alcuni aspetti dei dati, ad esempio l'ordine delle colonne restituite, potresti avere una brutta sorpresa una volta che la tabella viene riorganizzata e vengono aggiunte nuove colonne (o rimosse quelle esistenti)

  • in SQL Server (non sono sicuro di altri database), se hai bisogno di un sottoinsieme di colonne, c'è sempre la possibilità che un indice non cluster possa coprire quella richiesta (contiene tutte le colonne necessarie). Con a SELECT *, rinunci a questa possibilità fin dall'inizio. In questo caso particolare, i dati verrebbero recuperati dalle pagine dell'indice (se queste contengono tutte le colonne necessarie) e quindi l'I / O del disco e l'overhead della memoria sarebbero molto inferiori rispetto all'esecuzione di una SELECT *....query.

Sì, inizialmente è necessario un po 'più di digitazione (strumenti come SQL Prompt per SQL Server ti aiuteranno anche lì), ma questo è davvero un caso in cui c'è una regola senza eccezioni: non usare mai SELECT * nel tuo codice di produzione. MAI.


13
pur essendo d'accordo con te nella pratica, hai sicuramente ragione in tutti i casi quando prendi i dati delle colonne dalla tabella, come questa domanda si rivolge), la tua enfasi su MAI tuttavia mi spinge a sottolineare che queste regole non sono generali per TUTTE le query Sql .. in particolare, è utilizzato in una sottoquery dopo un predicato EXISTS, (come in Where Exists (Select * From ...) l'uso di non Select *è certamente un problema e in alcuni ambienti è considerata una best practice.
Charles Bretana

3
@Charles Bretana: sì, IF EXISTS(SELECT *...è un caso speciale - poiché lì, nessun dato viene realmente recuperato, ma è solo un controllo dell'esistenza, il SELECT * non è un problema lì ...
marc_s

1
E se sto sviluppando un'API che consente di recuperare i dati da una delle mie tabelle. Poiché non saprei a quali dati è interessato l'utente, suppongo che SELECT * sarebbe accettabile?
Simon Bengtsson

1
@SimonBengtsson: Vorrei comunque discutere contro questo - supponi di avere alcuni dati "amministrativi" in colonne specifiche della tua tabella che non vuoi esporre al cliente? Vorrei sempre specificare esplicitamente un elenco di colonne da recuperare
marc_s

1
È vero. E quando si interroga una vista che è stata specificatamente configurata per essere utilizzata con l'API?
Simon Bengtsson

21

Dovresti sempre solo selectle colonne di cui hai effettivamente bisogno. Non è mai meno efficiente selezionare meno invece di più, e si verificano anche meno effetti collaterali imprevisti, come accedere alle colonne dei risultati sul client lato indice, quindi fare in modo che quegli indici diventino errati aggiungendo una nuova colonna alla tabella.

[modifica]: significava accedere. Stupido cervello che si sveglia ancora.


3
+1 per un caso limite a cui credo non molti penseranno a prima vista: indici sul lato client e colonne aggiunte / modificate.
Tomas Aschan

1
Sì, ma l'uso di indici numerici per le colonne è così comune? Ho sempre avuto accesso ai dati delle colonne utilizzando chiavi stringa o nomi di proprietà se si utilizza ORM.
Lèse majesté

11
visto questo molto tempo fa, un programmatore junior ha selezionato * da una tabella e ha fatto ipotesi sull'ordine delle colonne; tutto il suo codice si è rotto non appena qualcun altro ha cambiato la tabella. Che divertimento abbiamo avuto.
Paul McKenzie

7
Probabilmente è una cattiva idea usare l'ordine delle colonne in generale solo per la leggibilità del codice, doppiamente cattiva da usare SELECT *con esso.
Lèse Majesté

2
Wow, l'accesso a colonne in base all'indice nel codice client sembra una straordinariamente cattiva idea. Del resto , fare affidamento sull'ordine in cui le colonne appaiono in un set di risultati mi sembra molto sporco.
Matt Peterson

7

A meno che tu non stia archiviando BLOB di grandi dimensioni, le prestazioni non sono un problema. Il motivo principale per non usare SELECT * è che se stai usando le righe restituite come tuple, le colonne tornano nell'ordine che lo schema specifica, e se questo cambia dovrai correggere tutto il tuo codice.

D'altra parte, se si utilizza l'accesso in stile dizionario, non importa in quale ordine tornano le colonne perché si accede sempre per nome.


6

Questo mi fa subito pensare a una tabella che stavo usando che conteneva una colonna di tipo blob; di solito conteneva un'immagine JPEG, di pochi Mbsecondi.

Inutile dire che non ho fatto SELECTquella colonna a meno che non ne avessi davvero bisogno. Avere quei dati in giro, specialmente quando ho selezionato più righe, era solo una seccatura.

Tuttavia, ammetto che altrimenti di solito cerco tutte le colonne in una tabella.


20
Le colonne LOB sono sempre il mio esempio preferito dei pericoli di SELECT *. Quindi stavo per votarti, finché non ho letto il terzo paragrafo. Tsk, tsk. Cosa succede se un altro sviluppatore aggiunge un BLOB a una tabella che attualmente non ha una colonna di questo tipo?
APC

1
@APC, vorrei poter votare di più il tuo commento. Pensa al tuo povero collega che vuole solo aggiungere una colonna senza causare un enorme crollo delle prestazioni! Pensa a quanto saranno arrabbiati quando scopriranno, dopo poche ore, la tua innocente selezione *.
Mike Sherov

1
@ user256007, sì, anche senza BLOB ... BLOB illustra solo l'esempio estremo. Controlla la mia risposta a Charles, ci sono momenti in cui la selezione di colonne specifiche può consentirti di prendere i dati dalla memoria senza nemmeno andare su disco!
Mike Sherov

1
@Richard, penso che siano ottimi quando l'ottimizzazione delle prestazioni del DB non è la tua preoccupazione principale, che è il 99% delle volte. Come con la maggior parte dei framework, tendono a generalizzare le cose per consentire uno sviluppo più veloce sacrificando le prestazioni pure. Come disse Knuth: "L'ottimizzazione prematura è la radice di tutti i mali". Quando arrivi al punto in cui devi preoccuparti delle prestazioni delle colonne selezionate rispetto a quelle selezionate *, (chiedi a Twitter informazioni su RoR) puoi preoccupartene e ottimizzarlo. Se il framework non è abbastanza robusto per supportarlo, direi che stai usando il framework sbagliato.
Mike Sherov il

1
@ user256007 - la regola generale è "non utilizzare SELECT *". La risposta di marc_s ha tutte le ragioni per cui è così.
APC

6

Durante una selezione SQL, il DB farà sempre riferimento ai metadati per la tabella, indipendentemente dal fatto che sia SELECT * per SELECT a, b, c ... Perché? Perché è lì che si trovano le informazioni sulla struttura e il layout della tabella nel sistema.

Deve leggere queste informazioni per due motivi. Uno, per compilare semplicemente la dichiarazione. È necessario assicurarsi di specificare almeno una tabella esistente. Inoltre, la struttura del database potrebbe essere cambiata dall'ultima volta che è stata eseguita un'istruzione.

Ora, ovviamente, i metadati del DB sono memorizzati nella cache nel sistema, ma è ancora l'elaborazione che deve essere eseguita.

Successivamente, i metadati vengono utilizzati per generare il piano di query. Ciò accade anche ogni volta che viene compilata un'istruzione. Di nuovo, funziona con i metadati memorizzati nella cache, ma è sempre fatto.

L'unica volta che questa elaborazione non viene eseguita è quando il database utilizza una query precompilata o ha memorizzato nella cache una query precedente. Questo è l'argomento per l'utilizzo di parametri di associazione piuttosto che SQL letterale. "SELECT * FROM TABLE WHERE key = 1" è una query diversa da "SELECT * FROM TABLE WHERE key =?" e l '"1" è vincolato alla chiamata.

I DB fanno molto affidamento sulla memorizzazione nella cache delle pagine per il loro lavoro. Molti DB moderni sono abbastanza piccoli da adattarsi completamente alla memoria (o, forse dovrei dire, la memoria moderna è abbastanza grande da contenere molti DB). Quindi il tuo costo I / O primario sul back-end è la registrazione e lo svuotamento della pagina.

Tuttavia, se stai ancora premendo il disco per il tuo DB, un'ottimizzazione primaria eseguita da molti sistemi è fare affidamento sui dati negli indici, piuttosto che sulle tabelle stesse.

Se hai:

CREATE TABLE customer (
    id INTEGER NOT NULL PRIMARY KEY,
    name VARCHAR(150) NOT NULL,
    city VARCHAR(30),
    state VARCHAR(30),
    zip VARCHAR(10));

CREATE INDEX k1_customer ON customer(id, name);

Quindi se fai "SELECT id, name FROM customer WHERE id = 1", è molto probabile che il tuo DB estragga questi dati dall'indice, piuttosto che dalle tabelle.

Perché? Probabilmente utilizzerà comunque l'indice per soddisfare la query (rispetto a una scansione della tabella), e anche se "nome" non è utilizzato nella clausola where, quell'indice sarà comunque l'opzione migliore per la query.

Ora il database ha tutti i dati necessari per soddisfare la query, quindi non c'è motivo di raggiungere le pagine della tabella stesse. L'utilizzo dell'indice comporta una riduzione del traffico su disco poiché si ha una maggiore densità di righe nell'indice rispetto alla tabella in generale.

Questa è una spiegazione ondulata di una specifica tecnica di ottimizzazione utilizzata da alcuni database. Molti hanno diverse tecniche di ottimizzazione e messa a punto.

Alla fine, SELECT * è utile per le query dinamiche che devi digitare a mano, non lo userei mai per "codice reale". L'identificazione delle singole colonne fornisce al DB più informazioni che può utilizzare per ottimizzare la query e offre un controllo migliore nel codice contro le modifiche allo schema, ecc.


Will, ho svalutato la tua risposta, solo perché usi NOT NULL insieme alla CHIAVE PRIMARIA. C'è una buona ragione per scrivere in questo modo?
Studente

4

Penso che non ci sia una risposta esatta per la tua domanda, perché hai riflettuto sulle prestazioni e facilità di manutenzione delle tue app. Select columnè più performante select *, ma se stai sviluppando un sistema di oggetti orientato, ti piacerà l'uso object.propertiese potresti aver bisogno di una proprietà in qualsiasi parte delle app, quindi avrai bisogno di scrivere più metodi per ottenere proprietà in situazioni speciali se non lo fai utilizzare select *e popolare tutte le proprietà. Le tue app devono avere buone prestazioni utilizzando select *e in alcuni casi dovrai usare la colonna di selezione per migliorare le prestazioni. Allora avrai la meglio su due mondi, possibilità di scrivere e mantenere app e prestazioni quando hai bisogno di prestazioni.


4

La risposta accettata qui è sbagliata. Mi sono imbattuto in questo quando un'altra domanda è stata chiusa come un duplicato di questa (mentre stavo ancora scrivendo la mia risposta - grr - quindi l'SQL sotto fa riferimento all'altra domanda).

Dovresti sempre usare SELECT attributo, attributo ... NON SELEZIONA *

È principalmente per problemi di prestazioni.

SELEZIONA il nome DAGLI utenti DOVE nome = 'John';

Non è un esempio molto utile. Considera invece:

SELECT telephone FROM users WHERE name='John';

Se è presente un indice su (nome, telefono), la query può essere risolta senza dover cercare i valori rilevanti dalla tabella: c'è un indice di copertura .

Inoltre, supponiamo che la tabella abbia un BLOB contenente un'immagine dell'utente, un CV caricato e un foglio di calcolo ... utilizzando SELECT * riporterà tutte queste informazioni nei buffer DBMS (forzando fuori altre informazioni utili dalla cache). Quindi verrà tutto inviato al client utilizzando il tempo di attività sulla rete e la memoria sul client per i dati ridondanti.

Può anche causare problemi funzionali se il client recupera i dati come un array enumerato (come mysql_fetch_array ($ x, MYSQL_NUM) di PHP). Forse quando il codice era scritto "telefono" era la terza colonna ad essere restituita da SELECT *, ma poi qualcuno arriva e decide di aggiungere un indirizzo email alla tabella, posizionato prima di "telefono". Il campo desiderato è ora spostato nella quarta colonna.


2

Ci sono ragioni per fare le cose in entrambi i casi. Uso molto SELECT * su PostgreSQL perché ci sono molte cose che puoi fare con SELECT * in PostgreSQL che non puoi fare con un elenco di colonne esplicito, in particolare quando nelle procedure memorizzate. Allo stesso modo in Informix, SELECT * su un albero di tabelle ereditato può fornire righe frastagliate mentre un elenco di colonne esplicito non può perché vengono restituite anche colonne aggiuntive nelle tabelle figlio.

Il motivo principale per cui lo faccio in PostgreSQL è che mi assicura di ottenere un tipo ben formato specifico per una tabella. Questo mi permette di prendere i risultati e usarli come tipo di tabella in PostgreSQL. Ciò consente anche molte più opzioni nella query rispetto a un elenco di colonne rigido.

D'altra parte, un elenco di colonne rigido fornisce un controllo a livello di applicazione che gli schemi db non siano cambiati in certi modi e questo può essere utile. (Faccio tali controlli su un altro livello.)

Per quanto riguarda le prestazioni, tendo a utilizzare VIEW e stored procedure che restituiscono tipi (e quindi un elenco di colonne all'interno della stored procedure). Questo mi dà il controllo su quali tipi vengono restituiti.

Ma tieni presente che sto usando SELECT * di solito su un livello di astrazione piuttosto che su tabelle di base.


2

Riferimento tratto da questo articolo:

Senza SELECT *: Quando si utilizza "SELECT *" in quel momento, si selezionano più colonne dal database e alcune di queste colonne potrebbero non essere utilizzate dall'applicazione. Ciò creerà costi e carichi aggiuntivi sul sistema di database e più dati viaggeranno attraverso la rete.

Con SELEZIONA *: Se hai requisiti speciali e hai creato un ambiente dinamico quando aggiungi o elimini una colonna, gestisci automaticamente il codice dell'applicazione. In questo caso speciale non è necessario modificare il codice dell'applicazione e del database e ciò influirà automaticamente sull'ambiente di produzione. In questo caso è possibile utilizzare "SELEZIONA *".


0

Solo per aggiungere una sfumatura alla discussione che non vedo qui: in termini di I / O, se stai usando un database con archiviazione orientata alle colonne puoi fare MOLTO meno I / O se interroghi solo per alcuni colonne. Quando passiamo agli SSD i vantaggi potrebbero essere un po 'più piccoli rispetto allo storage orientato alle righe, ma c'è a) solo la lettura dei blocchi che contengono le colonne che ti interessano b) la compressione, che generalmente riduce notevolmente la dimensione dei dati su disco e quindi il volume di dati letti dal disco.

Se non hai familiarità con l'archiviazione orientata alle colonne, un'implementazione per Postgres proviene da Citus Data, un'altra è Greenplum, un'altra Paraccel, un'altra (in senso lato) è Amazon Redshift. Per MySQL c'è Infobright, l'ormai defunto InfiniDB. Altre offerte commerciali includono Vertica di HP, Sybase IQ, Teradata ...


-1
select * from table1 INTERSECT  select * from table2

pari

select distinct t1 from table1 where Exists (select t2 from table2 where table1.t1 = t2 )

Potresti formattare il tuo codice evidenziandolo e premendo Ctrl + K
WhatsThePoint
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.