Qual è il motivo per non usare select *?


136

Ho visto diverse persone affermare che dovresti nominare in modo specifico ogni colonna che desideri nella tua query di selezione.

Supponendo che userò comunque tutte le colonne, perché non dovrei usare SELECT *?

Anche considerando la domanda * Query SQL - Seleziona * dalla vista o Seleziona col1, col2, ... colN dalla vista *, non penso che questo sia un duplicato esatto in quanto sto affrontando il problema da una prospettiva leggermente diversa.

Uno dei nostri principi è di non ottimizzare prima che sia il momento. Con questo in mente, sembra che l'uso SELECT *dovrebbe essere il metodo preferito fino a quando non viene dimostrato che è un problema di risorse o lo schema è praticamente impostato su pietra. Che, come sappiamo, non avverrà fino a quando lo sviluppo non sarà completamente completato.

Detto questo, c'è un problema prioritario da non usare SELECT *?

Risposte:


168

L'essenza della citazione di non ottimizzare prematuramente è cercare un codice semplice e diretto e quindi utilizzare un profiler per evidenziare i punti caldi, che è quindi possibile ottimizzare per essere efficienti.

Quando usi select *, rendi impossibile creare un profilo, quindi non stai scrivendo un codice chiaro e diretto e stai andando contro lo spirito della citazione. select *è un anti-pattern.


Quindi selezionare le colonne non è un'ottimizzazione prematura. Alcune cose dalla cima della mia testa ....

  1. Se si specificano le colonne in un'istruzione SQL, il motore di esecuzione SQL genererà un errore se tale colonna viene rimossa dalla tabella e la query viene eseguita.
  2. Puoi scansionare più facilmente il codice dove viene utilizzata quella colonna.
  3. Dovresti sempre scrivere domande per riportare la minima quantità di informazioni.
  4. Come altri menzionano se usi l'accesso alla colonna ordinale non dovresti mai usare seleziona *
  5. Se l'istruzione SQL unisce le tabelle, selezionare * fornisce tutte le colonne di tutte le tabelle nell'unione

Il corollario è che usando select *...

  1. Le colonne utilizzate dall'applicazione sono opache
  2. I DBA e i loro profili di query non sono in grado di aiutare le scarse prestazioni dell'applicazione
  3. Il codice è più fragile quando si verificano cambiamenti
  4. Il tuo database e la tua rete soffrono perché stanno riportando troppi dati (I / O)
  5. Le ottimizzazioni del motore di database sono minime poiché stai riportando tutti i dati indipendentemente (logico).

Scrivere SQL corretto è facile quanto scrivere Select *. Quindi la vera persona pigra scrive SQL corretto perché non vuole rivisitare il codice e provare a ricordare cosa stavano facendo quando lo hanno fatto. Non vogliono spiegare ai DBA ogni bit di codice. Non vogliono spiegare ai loro clienti perché l'applicazione funziona come un cane.


2
Nella prima sezione, il punto 5 dovrebbe leggere "select * fornisce tutte le colonne di tutte le tabelle nel join". Nella tua seconda sezione, i punti # 2 e # 5 non sono necessariamente veri e non devono essere elencati come motivi per non usare "select *".
Jimmyorr,

1
@uglysmurf - grazie per la correzione, ma per quanto riguarda i 2 e 5 - anche se potrebbero non essere necessariamente veri per tutti i database / dba in tutti i casi, ritengo che siano importanti e validi per la maggior parte dei casi e li lasceranno dentro. L'uso di 'select *' non ha mai semplificato il lavoro di un dba.
Robert Paulson,

11
Direi che il n. 3 (codice fragile) non è proprio vero. A seconda dell'implementazione, Seleziona * potrebbe renderlo MENO fragile, ma non vedo come potrebbe essere di più.
JohnFx,

2
@JohnFx, immagino che tu definisca fragile in modo diverso. Il fragile è normalmente definito come "si rompe facilmente". Avere dipendenze sconosciute o difficili da trovare perché ogni parte di codice utilizzerà colonne diverse significa che non posso facilmente cambiare nulla a livello di dati senza una regressione completa ... il che sembra fragile.
Robert Paulson,

9
@mavnn, wrt fragilità, temo che questo si trasformi in una questione semantica sulla mia scelta della parola fragile. La mia ultima parola è dire che fa comunque poca differenza. L'unico scenario sono le colonne rinominate / rimosse. Stai solo spostando l'interruzione da quando viene eseguito (esplicito) sql all'interruzione quando i risultati vengono consumati. Il modo in cui viene consumato il risultato della query può variare e il codice potrebbe non riuscire in modo silenzioso, ma il motore di esecuzione sql fallirà sicuramente con sql non valido. Quindi selezionare * ti ha aiutato? L'errore esplicito IMO più vicino al DB per un problema DB è migliore. Grazie
Robert Paulson,

42

Se il codice dipende dal fatto che le colonne siano in un ordine specifico, il codice si interromperà in caso di modifiche alla tabella. Inoltre, potresti recuperare troppo dalla tabella quando selezioni *, specialmente se nella tabella è presente un campo binario.

Solo perché ora stai utilizzando tutte le colonne, ciò non significa che qualcun altro non aggiungerà una colonna aggiuntiva alla tabella.

Aggiunge inoltre un sovraccarico alla cache di esecuzione del piano poiché deve recuperare i metadati sulla tabella per sapere quali colonne sono presenti *.


4
Buona risposta, ma cambierei il "codice si interromperà" in "Il codice potrebbe rompersi". Questo è il vero problema qui, l'uso "select *" non produce SEMPRE un cambiamento radicale. E quando si verifica l'interruzione di solito è fortemente disaccoppiata dall'uso che finisce per spezzarsi.
BQ.

4
Se qualcuno fa riferimento a colonne normalmente nel proprio codice, si trovano in difficoltà indipendentemente dal fatto che utilizzino SELECT * o meno. Il sovraccarico dell'esecuzione del piano è banale e non importerebbe comunque una volta che il piano è stato memorizzato nella cache.
MusiGenesis,

1
Quindi l'errore del programmatore risiede nella scrittura del codice che dipende dalla sequenza delle colonne. Non hai mai bisogno di farlo.
dkretz,

1
@doofledorfer - non dire mai mai. È più veloce accedere alle colonne ordinali ed è pratico a volte. L'uso di select * è un errore maggiore di quello dell'uso ordinale.
Robert Paulson,

23

Uno dei motivi principali è che se si aggiungono / rimuovono colonne dalla tabella, qualsiasi query / procedura che sta effettuando una chiamata SELECT * ora riceverà più o meno colonne di dati del previsto.


3
Non scrivere mai codice che dipende comunque dal numero di colonne restituite.
dkretz,

4
Ma tutti scrivono codice che richiede ai programmatori di sapere quali dati stanno tornando. Non puoi Ctrl + F il nome della tua colonna se è nascosto in un SELEZIONA *.
Lotus Notes,

17
  1. In modo rotatorio stai infrangendo la regola della modularità sull'uso della tipizzazione rigorosa, ove possibile. Explicit è quasi universalmente migliore.

  2. Anche se ora hai bisogno di ogni colonna nella tabella, ne potrebbero essere aggiunte altre in seguito, che verranno rimosse ogni volta che esegui la query e potrebbero compromettere le prestazioni. Danneggia le prestazioni perché

    • Stai estraendo più dati dal cavo; e
    • Perché potresti vanificare la capacità dell'ottimizzatore di estrarre i dati dall'indice (per le query su colonne che fanno tutti parte di un indice.) Anziché effettuare una ricerca nella tabella stessa

Quando usare selezionare *

Quando hai BISOGNO esplicitamente di ogni colonna nella tabella, invece di aver bisogno di ogni colonna nella tabella CHE ESISTE AL MOMENTO CHE HAI INTERROTATO LA QUERY. Ad esempio, se si stesse scrivendo un'app di gestione DB che doveva visualizzare l'intero contenuto della tabella (qualunque cosa fosse), è possibile utilizzare tale approccio.


1
Un altro momento da utilizzare SELECT *sarebbe quando si eseguono query di prova utilizzando il client db.
cdmckay,

Sembra una strana eccezione dato il contesto della domanda. Oltre a salvare un po 'di digitazione, qual è il vantaggio di farlo per le query di prova?
JohnFx,

Anche SELEZIONA * DA (SELEZIONA una tabella DA, b, c DA) è OK.
kmkaplan,

12

Ci sono alcuni motivi:

  1. Se il numero di colonne in un database cambia e l'applicazione prevede che ci sia un certo numero ...
  2. Se l'ordine delle colonne in un database cambia e l'applicazione si aspetta che siano in un certo ordine ...
  3. Sovraccarico di memoria. 8 colonne INTEGER non necessarie aggiungerebbero 32 byte di memoria sprecata. Non suona molto, ma questo vale per ogni query e INTEGER è uno dei tipi di colonne piccole ... le colonne extra hanno più probabilità di essere colonne VARCHAR o TEXT, che si sommano più velocemente.
  4. Overhead di rete. Relativo al sovraccarico di memoria: se eseguo 30.000 query e ho 8 colonne INTEGER non necessarie, ho sprecato 960 KB di larghezza di banda. Le colonne VARCHAR e TEXT sono probabilmente molto più grandi.

Nota: ho scelto INTEGER nell'esempio sopra perché hanno una dimensione fissa di 4 byte.


1 e 2 sarebbero un odore di codice e 3 e 4
sembrerebbero

7

Se l'applicazione ottiene i dati con SELECT * e la struttura della tabella nel database viene modificata (supponiamo che una colonna venga rimossa), l'applicazione avrà esito negativo in ogni posizione in cui si fa riferimento al campo mancante. Se invece includi tutte le colonne nella tua query, la tua applicazione si interromperà (si spera) in un punto in cui inizialmente ottieni i dati, rendendo più semplice la correzione.

Detto questo, ci sono diverse situazioni in cui SELECT * è desiderabile. Una è una situazione che incontro continuamente, in cui ho bisogno di replicare un'intera tabella in un altro database (ad esempio da SQL Server a DB2). Un'altra è un'applicazione scritta per visualizzare le tabelle in modo generico (cioè senza alcuna conoscenza di una tabella particolare).


La domanda non è 'è selezionato * sempre desiderabile', quindi la seconda parte della tua risposta è irrilevante. La domanda afferma che dovrebbe essere preferibile usare 'select *', che ovviamente è completo.
Robert Paulson,

Sì, la mia seconda parte è irrilevante. OQ ha cambiato la domanda per indicare che SELECT * è preferibile, e sì, è un po 'bizzarro.
MusiGenesis,

Ah sì scusa - la domanda ha cambiato direzione dopo la tua risposta.
Robert Paulson,

Va bene. Anche Mozart era un editore ( stackoverflow.com/questions/292682/… ). Il mio post originale ha suggerito che l'uso di SELECT * ha portato al cannibalismo. :)
MusiGenesis,

3

In realtà ho notato uno strano comportamento quando ho usato le select *viste in SQL Server 2005.

Esegui la seguente query e vedrai cosa intendo.

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','c1'
union all select 'a2','b2','c2'
union all select 'a3','b3','c3'

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vStartest]'))
DROP VIEW [dbo].[vStartest]
go
create view dbo.vStartest as
select * from dbo.starTest
go

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vExplicittest]'))
DROP VIEW [dbo].[vExplicittest]
go
create view dbo.[vExplicittest] as
select a,b,c from dbo.starTest
go


select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicitTest

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [D] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','d1','c1'
union all select 'a2','b2','d2','c2'
union all select 'a3','b3','d3','c3'

select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicittest

Confronta i risultati delle ultime 2 dichiarazioni selezionate. Credo che ciò che vedrai è il risultato del fatto che Select * fa riferimento alle colonne per indice anziché per nome.

Se ricostruisci la vista, funzionerà di nuovo bene.

MODIFICARE

Ho aggiunto una domanda separata, * "seleziona * dalla tabella" vs "seleziona colA, colB, ecc. Dalla tabella" comportamento interessante in SQL Server 2005 * per esaminare tale comportamento in modo più dettagliato.


2

È possibile unire due tabelle e utilizzare la colonna A dalla seconda tabella. Se successivamente aggiungi la colonna A alla prima tabella (con lo stesso nome ma probabilmente con un significato diverso) molto probabilmente otterrai i valori dalla prima tabella e non dalla seconda come in precedenza. Ciò non accadrà se specifichi esplicitamente le colonne che desideri selezionare.

Naturalmente, specificare le colonne a volte causa anche bug se si dimentica di aggiungere le nuove colonne a ogni clausola select. Se la nuova colonna non è necessaria ogni volta che viene eseguita la query, potrebbe essere necessario del tempo prima che il bug venga notato.


2

Capisco dove stai andando per quanto riguarda l'ottimizzazione prematura, ma questo in realtà arriva solo a un punto. L'intento è di evitare l' ottimizzazione non necessaria all'inizio. I tuoi tavoli non sono indicizzati? Utilizzeresti nvarchar (4000) per memorizzare un codice postale?

Come altri hanno sottolineato, ci sono altri aspetti positivi nello specificare ogni colonna che si intende utilizzare nella query (come la manutenibilità).


2

Quando specifichi le colonne, ti leghi anche a una serie specifica di colonne e ti rendi meno flessibile, facendo sì che Feuerstein si capovolga, beh, ovunque si trovi. Solo un pensiero.


1
Non ho assolutamente idea di chi sia Feuerstein. Ho provato a cercare su Google e ho trovato uno psicologo, un personaggio televisivo e un blogger, quindi il meglio che ho potuto inventare era uno scherzo.
NotMe

Autore dei libri O'Reilly su PL / SQL. Prova a cercare "feuerstein sql" su google invece di "feuerstein".
orbfish,

2

SELEZIONA * non è sempre malvagio. Secondo me, almeno. Lo uso abbastanza spesso per query dinamiche che restituiscono un'intera tabella, oltre ad alcuni campi calcolati.

Ad esempio, voglio calcolare le geometrie geografiche da una tabella "normale", ovvero una tabella senza alcun campo di geometria, ma con campi contenenti coordinate. Uso postgresql e i suoi postgis di estensione spaziale. Ma il principio si applica per molti altri casi.

Un esempio:

  • una tabella dei luoghi, con coordinate memorizzate nei campi etichettati x, y, z:

    CREATE TABLE luoghi (numero intero_ideale, x numerico (10, 3), y numerico (10, 3), z numerico (10, 3), descrizione varchar);

  • alimentiamolo con alcuni valori di esempio:

    INSERISCI IN Place (id_ideale, x, y, z, descrizione) VALORI
    (1, 2.295, 48.863, 64, 'Parigi, Place de l \' Étoile '),
    (2, 2.945, 48.858, 40,' Parigi, Tour Eiffel "),
    (3, 0.373, 43.958, 90," Preservativo, Cathédrale St-Pierre ");

  • Voglio essere in grado di mappare i contenuti di questa tabella, usando alcuni client GIS. Il modo normale è aggiungere un campo di geometria alla tabella e costruire la geometria, in base alle coordinate. Preferirei invece ottenere una query dinamica: in questo modo, quando cambio coordinate (correzioni, maggiore precisione, ecc.), Gli oggetti mappati si muovono effettivamente, dinamicamente. Quindi, ecco la query con SELECT * :

    CREA O SOSTITUISCI VISUALIZZA luoghi_punti COME
    SELEZIONA *,
    GeomFromewkt ('SRID = 4326; POINT (' || x || '' || y || '' || z || ')')
    DA luoghi;

    Fare riferimento a PostGis, per l'uso della funzione GeomFromewkt ().

  • Ecco il risultato:

    SELEZIONA * DA luoghi_punti;

place_id | x | y | z | descrizione | geomfromewkt                            
---------- + ------- + -------- + -------- + ------------- ----------------- + -------------------------------- ------------------------------------  
        1 | 2.295 | 48.863 | 64.000 | Parigi, Place de l'Étoile | 01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040  
        2 | 2.945 | 48.858 | 40.000 | Parigi, Tour Eiffel | 01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440
        3 | 0.373 | 43.958 | 90.000 | Preservativo, Cathédrale St-Pierre | 01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640
(3 linee)

La colonna più a destra ora può essere utilizzata da qualsiasi programma GIS per mappare correttamente i punti.

  • Se, in futuro, alcuni campi verranno aggiunti alla tabella: nessun problema, devo solo eseguire di nuovo la stessa definizione VIEW.

Vorrei che la definizione di VIEW potesse essere mantenuta "così com'è", con *, ma non è così: è così che viene memorizzata internamente da postgresql:

SELEZIONA places.place_id, places.x, places.y, places.z, places.description, geomfromewkt ((((((((SRID = 4326; POINT (':: text || places.x) ||' ': : testo) || places.y) || '' :: text) || places.z) || ')' :: text) COME geomfromewkt DA luoghi;


1

Anche se si utilizza ogni colonna ma si indirizza l'array di righe in base all'indice numerico, si avranno problemi se si aggiunge successivamente un'altra riga.

Quindi sostanzialmente si tratta di manutenibilità! Se non usi il selettore * non dovrai preoccuparti delle tue domande.


1

Selezionando solo le colonne necessarie, il set di dati in memoria sarà più piccolo e, di conseguenza, l'applicazione sarà più veloce.

Inoltre, molti strumenti (ad es. Stored procedure) eseguono anche piani di esecuzione delle query nella cache. Se successivamente aggiungi o rimuovi una colonna (particolarmente facile se stai selezionando una vista), lo strumento spesso commetterà errori quando non ottiene i risultati che si aspetta.


1

Rende il tuo codice più ambiguo e più difficile da mantenere; perché stai aggiungendo ulteriori dati inutilizzati al dominio e non è chiaro quale sia la tua intenzione e quale no. (Suggerisce anche che potresti non saperlo o preoccupartene.)


1

Per rispondere direttamente alla domanda: non utilizzare "SELEZIONA *" quando rende il codice più fragile alle modifiche alle tabelle sottostanti. Il codice dovrebbe interrompersi solo quando viene apportata una modifica alla tabella che influisce direttamente sui requisiti del programma.

L'applicazione dovrebbe sfruttare il livello di astrazione fornito dall'accesso relazionale.


1

Non uso SELECT * semplicemente perché è bello vedere e sapere quali campi sto recuperando.


1

Generalmente è male usare 'select *' all'interno delle viste perché sarai costretto a ricompilare la vista in caso di modifica della colonna della tabella. Modificando le colonne della tabella sottostante di una vista si otterrà un errore per le colonne inesistenti fino a quando non si torna indietro e si ricompila.


1

Va bene quando lo fai exists(select * ...)perché non si espande mai. Altrimenti è davvero utile solo quando si esplorano le tabelle con istruzioni di selezione temporanee o se si dispone di un CTE definito sopra e si desidera ogni colonna senza digitarle di nuovo tutte.


1

Solo per aggiungere una cosa che nessun altro ha menzionato. Select *restituisce tutte le colonne, qualcuno potrebbe aggiungere in seguito una colonna che non vuoi necessariamente che gli utenti siano in grado di vedere, ad esempio chi ha aggiornato i dati per l'ultima volta o un timestamp o note che solo i gestori dovrebbero vedere non tutti gli utenti, ecc.

Inoltre, quando si aggiunge una colonna, l'impatto sul codice esistente dovrebbe essere rivisto e considerato per vedere se sono necessarie modifiche in base alle informazioni archiviate nella colonna. Usando select *, quella recensione verrà spesso saltata perché lo sviluppatore supporrà che nulla si romperà. E in effetti nulla può sembrare esplicitamente rompersi, ma le query possono ora iniziare a restituire la cosa sbagliata. Solo perché nulla si rompe esplicitamente, non significa che non ci dovrebbero essere state modifiche alle query.


0

perché "select *" sprecherà memoria quando non sono necessari tutti i campi. Ma per SQL Server, le loro prestazioni sono le stesse.

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.