sélectionnez * vs sélectionnez la colonne


124

Si j'ai juste besoin de 2/3 colonnes et que j'interroge SELECT *au lieu de fournir ces colonnes dans la requête de sélection, y a-t-il une dégradation des performances concernant plus / moins d'E / S ou de mémoire?

La surcharge du réseau peut être présente si je sélectionne * sans besoin.

Mais dans une opération de sélection, le moteur de base de données extrait-il toujours le tuple atomique du disque, ou extrait-il uniquement les colonnes demandées dans l'opération de sélection?

S'il extrait toujours un tuple, la surcharge d'E / S est la même.

Dans le même temps, il peut y avoir une consommation de mémoire pour supprimer les colonnes demandées du tuple, s'il extrait un tuple.

Donc si c'est le cas, select someColumn aura plus de surcharge de mémoire que celui de select *


Avez-vous des questions sur un SGBDR spécifique? Il est possible que la manière dont les SELECTrequêtes sont exécutées / traitées diffère d'une base de données à l'autre.
Lèse majesté

10
En passant, dans PostgreSQL, si vous dites CREATE VIEW foo_view AS SELECT * FROM foo;, ajoutez des colonnes à la table foo plus tard, ces colonnes n'apparaîtront pas automatiquement dans foo_view comme prévu. En d'autres termes, *dans ce contexte ne se développe qu'une seule fois (au moment de la création de la vue), et non par SELECT. En raison des complications découlant d'ALTER TABLE, je dirais que (en pratique) *est considéré comme nocif.
Joey Adams

@JoeyAdams - pas seulement PostgresQL, c'est aussi le comportement d'Oracle.
APC

1
@OMG Ponies: Je n'étais pas au courant d'un article similaire. Cependant, ceux-ci ne sont pas vraiment similaires. @ Lèse majesté: Je parle de SGBDR générique. pas à propos d'un fournisseur spécifique @Joey Adams: Hmm je sais que * n'est pas sûr. veux juste discuter des problèmes de performances concernant.
Neel Basu

Réponses:


31

Il tire toujours un tuple (sauf dans les cas où le tableau a été segmenté verticalement - divisé en morceaux de colonnes), donc, pour répondre à la question que vous avez posée, cela n'a pas d'importance du point de vue des performances. Cependant, pour de nombreuses autres raisons (ci-dessous), vous devez toujours sélectionner spécifiquement les colonnes que vous souhaitez, par nom.

Il extrait toujours un tuple, car (dans le SGBDR de tous les fournisseurs avec lequel je suis familier), la structure de stockage sur disque sous-jacente pour tout (y compris les données de table) est basée sur des pages d'E / S définies (dans SQL Server par exemple, chaque page est 8 kilo-octets). Et chaque lecture ou écriture d'E / S se fait par page. Par exemple, chaque écriture ou lecture est une page complète de données.

En raison de cette contrainte structurelle sous-jacente, une conséquence est que chaque ligne de données dans une base de données doit toujours être sur une et une seule page. Il ne peut pas s'étendre sur plusieurs pages de données (sauf pour des choses spéciales comme les blobs, où les données blob réelles sont stockées dans des segments de page séparés, et la colonne de ligne de la table réelle ne reçoit alors qu'un pointeur ...). Mais ces exceptions ne sont que cela, des exceptions, et ne s'appliquent généralement pas sauf dans des cas particuliers (pour des types spéciaux de données, ou certaines optimisations pour des circonstances spéciales)
Même dans ces cas particuliers, généralement, la ligne de données du tableau elle-même (qui contient le pointeur vers les données réelles du Blob, ou autre), il doit être stocké sur une seule page IO ...

EXCEPTION. Le seul endroit oùSelect * est OK, est dans la sous-requête après une clause Existsou Not Existspredicate, comme dans:

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

EDIT: Pour répondre au commentaire de @Mike Sherer, oui c'est vrai, à la fois techniquement, avec un peu de définition pour votre cas particulier, et esthétiquement. Premièrement, même lorsque l'ensemble de colonnes demandé est un sous-ensemble de celles stockées dans un index, le processeur de requêtes doit extraire toutes les colonnes stockées dans cet index, pas seulement celles demandées, pour les mêmes raisons - TOUTES les E / S doivent être effectuées dans et les données d'index sont stockées dans les pages IO, tout comme les données de table. Donc, si vous définissez "tuple" pour une page d'index comme l'ensemble de colonnes stockées dans l'index, l'instruction est toujours vraie.
et la déclaration est vraie esthétiquement parce que le fait est qu'elle récupère les données en fonction de ce qui est stocké dans la page d'E / S, pas de ce que vous demandez, et ceci est vrai que vous accédiez à la page d'E / S de la table de base ou à un index Page E / S.

Pour d'autres raisons de ne pas utiliser Select *, consultez Pourquoi est-il SELECT *considéré comme dangereux? :


"Il tire toujours un tuple" êtes-vous sûr? Hmm Okay donc j'avais raison. si c'est le cas, il select *y aura moins de surcharge de mémoire que select columnmais la même surcharge d'E / S. donc si nous laissons la surcharge du réseau. select *si moins de frais généraux que celui deselect column
Neel Basu

10
Ce n'est pas vrai. Un exemple qui me vient à l'esprit est que lorsque vous ne voulez que la valeur d'une colonne indexée dans MySQL (par exemple, juste pour vérifier l'existence d'une ligne), et que vous utilisez le moteur de stockage MyISAM, il récupérera les données du MYI, qui pourrait être en mémoire, et même pas aller sur le disque!
Mike Sherov

Ya si l'ensemble demandé de tuple est en mémoire, il n'y aura pas d'E / S mais c'est un cas particulier. Alors quel est l'été. Si je sélectionne une colonne indexée, tout le tuple n'est pas lu? sinon le tuple entier est lu?
Neel Basu

Je ne sais pas exactement comment MySql fait la mise en cache, mais dans SQL Server et dans Oracle, même lorsque les données sont dans le cache en mémoire, il y accède toujours en utilisant la même structure de page que lors de l'accès à partir du disque. ce qui signifie qu'il faudrait une E / S de mémoire par page de données ... exactement comme il le ferait à partir du disque. (sauf que les E / S mémoire sont bien plus rapides que les E / S disque). En effet, c'est un objectif de la conception de la mise en cache, pour rendre le processus d'accès totalement indépendant de l'emplacement des données.
Charles Bretana

2
Pouvez-vous préciser davantage «pour de nombreuses autres raisons»? Parce que cela n'était pas clair pour moi. Si les performances n'ont pas d'importance, pourquoi se soucier de demander des noms de colonnes?
Dennis

111

Il y a plusieurs raisons pour lesquelles vous ne devriez jamais (jamais) utiliser SELECT *dans le code de production:

  • puisque vous ne donnez aucune indication à votre base de données sur ce que vous voulez, elle devra d'abord vérifier la définition de la table afin de déterminer les colonnes de cette table. Cette recherche coûtera du temps - pas beaucoup en une seule requête - mais elle s'additionne avec le temps

  • si vous n'avez besoin que de 2/3 des colonnes, vous sélectionnez 1/3 de trop de données qui doivent être extraites du disque et envoyées sur le réseau

  • si vous commencez à vous fier à certains aspects des données, par exemple l'ordre des colonnes renvoyées, vous pourriez avoir une mauvaise surprise une fois que la table est réorganisée et que de nouvelles colonnes sont ajoutées (ou les colonnes existantes supprimées)

  • dans SQL Server (pas sûr des autres bases de données), si vous avez besoin d'un sous-ensemble de colonnes, il y a toujours une chance qu'un index non clusterisé couvre cette demande (contient toutes les colonnes nécessaires). Avec a SELECT *, vous renoncez à cette possibilité dès le départ. Dans ce cas particulier, les données seraient récupérées à partir des pages d'index (si celles-ci contiennent toutes les colonnes nécessaires) et donc les E / S disque et la surcharge de mémoire seraient beaucoup moins importantes par rapport à une SELECT *....requête.

Oui, cela prend un peu plus de frappe au départ (des outils comme SQL Prompt pour SQL Server vous aideront même là-bas) - mais c'est vraiment un cas où il y a une règle sans aucune exception: n'utilisez jamais SELECT * dans votre code de production. DÉJÀ.


13
tout en étant d'accord avec vous dans la pratique, vous avez certainement raison dans tous les cas lors de la récupération des données de colonne de la table, car cette question aborde), l'accent mis sur EVER m'amène néanmoins à souligner que ces règles ne sont pas générales pour TOUTES les requêtes SQL. En particulier, il est utilisé dans une sous-requête après un prédicat EXISTS, (comme dans Where Exists (Select * From ...) l'utilisation de Select *n'est certainement pas un problème, et dans certains cercles est considérée comme une meilleure pratique.
Charles Bretana

3
@Charles Bretana: oui, IF EXISTS(SELECT *...c'est un cas particulier - puisque là, aucune donnée n'est vraiment récupérée, mais c'est juste un contrôle d'existence, le SELECT * n'est pas un problème là-bas ...
marc_s

1
Et si je développe une API qui permet de récupérer des données à partir d'une de mes tables. Puisque je ne saurais pas quelles données l'utilisateur est intéressé, je suppose que SELECT * serait acceptable?
Simon Bengtsson

1
@SimonBengtsson: Je dirais toujours contre cela - supposons que vous ayez des données "administratives" dans des colonnes spécifiques de votre table que vous ne voulez pas exposer au client? Je spécifierais toujours explicitement une liste de colonnes à récupérer
marc_s

1
C'est vrai. Qu'en est-il lors de l'interrogation d'une vue qui a été spécifiquement configurée pour être utilisée avec l'API?
Simon Bengtsson

21

Tu devrais toujours seulementselect utiliser les colonnes dont vous avez réellement besoin. Il n'est jamais moins efficace de sélectionner moins au lieu de plus, et vous rencontrez également moins d'effets secondaires inattendus - comme accéder à vos colonnes de résultats côté client par index, puis rendre ces index incorrects en ajoutant une nouvelle colonne à la table.

[modifier]: signifiait accès. Le cerveau stupide se réveille toujours.


3
+1 pour un cas de pointe auquel je pense que peu de gens penseront à première vue - index du côté client et colonnes ajoutées / modifiées.
Tomas Aschan

1
Oui, mais l'utilisation d'indices numériques pour les colonnes est-elle courante? J'ai toujours accédé aux données de colonne à l'aide de clés de chaîne ou de noms de propriété si vous utilisez ORM.
Lèse majesté

11
vu cela il y a longtemps, le programmeur junior a sélectionné * à partir d'une table et a fait des hypothèses sur l'ordre des colonnes; tout son code s'est cassé dès que quelqu'un d'autre a changé la table. Quel plaisir nous avons eu.
Paul McKenzie

7
C'est probablement une mauvaise idée d'utiliser l'ordre des colonnes en général juste pour la lisibilité du code, doublement mauvaise à utiliser SELECT *avec lui.
Lèse majesté

2
Wow, l' accès à des colonnes par index dans le code client apparaît comme une phénoménalement mauvaise idée. Pour cette question, en se fondant sur l'ordre dans lequel les colonnes apparaissent dans un jeu de résultats de quelque façon que se sent très sale pour moi.
Matt Peterson

7

Sauf si vous stockez de gros blobs, les performances ne sont pas un problème. La principale raison de ne pas utiliser SELECT * est que si vous utilisez des lignes retournées comme tuples, les colonnes reviennent dans l'ordre que le schéma spécifie, et si cela change, vous devrez corriger tout votre code.

D'un autre côté, si vous utilisez un accès de style dictionnaire, l'ordre dans lequel les colonnes reviennent n'a pas d'importance car vous y accédez toujours par nom.


6

Cela me fait immédiatement penser à une table que j'utilisais et qui contenait une colonne de type blob; il contenait généralement une image JPEG, de quelques Mbs.

Inutile de dire que je ne l' ai pas SELECTcette colonne à moins que je vraiment besoin. Avoir ces données flottantes - en particulier lorsque j'ai sélectionné plusieurs lignes - n'était qu'un problème.

Cependant, j'admets que je recherche généralement toutes les colonnes d'une table.


20
Les colonnes LOB sont toujours mon exemple préféré des dangers de SELECT *. J'étais donc sur le point de vous voter jusqu'à ce que je lis le troisième paragraphe. TTT ... TTT. Que se passe-t-il si un autre développeur ajoute un BLOB à une table qui n'a actuellement pas une telle colonne?
APC

1
@APC, j'aimerais pouvoir voter davantage pour votre commentaire. Pensez à votre pauvre collègue qui veut juste ajouter une colonne sans provoquer une énorme effondrement des performances! Pensez à leur colère quand ils découvriront après quelques heures votre sélection innocente *.
Mike Sherov

1
@ user256007, oui, même sans BLOB ... BLOB illustre juste l'exemple extrême. Vérifiez ma réponse à Charles, il y a des moments où la sélection de colonnes spécifiques peut vous permettre de récupérer les données de la mémoire sans même aller sur le disque!
Mike Sherov

1
@Richard, je pense qu'ils sont parfaits lorsque l'optimisation des performances de la base de données n'est pas votre principale préoccupation, ce qui est 99% du temps. Comme avec la plupart des frameworks, ils ont tendance à généraliser les choses pour permettre un développement plus rapide tout en sacrifiant les performances pures. Comme l'a dit Knuth: "L'optimisation prématurée est la racine de tout mal." Lorsque vous arrivez au point où vous devez vous soucier des performances de certaines colonnes par rapport à select *, (demandez à Twitter à propos du RoR), vous pouvez vous en préoccuper et l'optimiser ensuite. Si le cadre n'est pas assez robuste pour prendre en charge cela, je dirais que vous utilisez le mauvais cadre.
Mike Sherov du

1
@ user256007 - la règle générale est "ne pas utiliser SELECT * '. La réponse de marc_s a toutes les raisons pour lesquelles c'est le cas.
APC

6

Lors d'une sélection SQL, la base de données va toujours se référer aux métadonnées de la table, qu'il s'agisse de SELECT * pour SELECT a, b, c ... Pourquoi? Parce que c'est là que se trouvent les informations sur la structure et la disposition de la table sur le système.

Il doit lire ces informations pour deux raisons. Un, pour simplement compiler la déclaration. Il doit s'assurer que vous spécifiez au moins une table existante. En outre, la structure de la base de données peut avoir changé depuis la dernière exécution d'une instruction.

Maintenant, évidemment, les métadonnées DB sont mises en cache dans le système, mais c'est encore un traitement qui doit être effectué.

Ensuite, les métadonnées sont utilisées pour générer le plan de requête. Cela se produit également à chaque fois qu'une instruction est compilée. Encore une fois, cela fonctionne avec les métadonnées mises en cache, mais c'est toujours fait.

Le seul moment où ce traitement n'est pas effectué est lorsque la base de données utilise une requête précompilée ou a mis en cache une requête précédente. C'est l'argument pour utiliser des paramètres de liaison plutôt que du SQL littéral. "SELECT * FROM TABLE WHERE key = 1" est une requête différente de "SELECT * FROM TABLE WHERE key =?" et le "1" est lié à l'appel.

Les bases de données reposent fortement sur la mise en cache des pages pour leur travail. De nombreuses bases de données modernes sont suffisamment petites pour tenir complètement en mémoire (ou, peut-être devrais-je dire, la mémoire moderne est suffisamment grande pour contenir de nombreuses bases de données). Ensuite, votre principal coût d'E / S sur le back-end est la journalisation et les vidages de page.

Cependant, si vous utilisez toujours le disque de votre base de données, une optimisation principale effectuée par de nombreux systèmes consiste à s'appuyer sur les données des index, plutôt que sur les tables elles-mêmes.

Si tu as:

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);

Ensuite, si vous faites "SELECT id, nom FROM customer WHERE id = 1", il est très probable que votre DB extraira ces données de l'index, plutôt que des tables.

Pourquoi? Il utilisera probablement l'index de toute façon pour satisfaire la requête (par rapport à une analyse de table), et même si 'nom' n'est pas utilisé dans la clause where, cet index sera toujours la meilleure option pour la requête.

Maintenant, la base de données a toutes les données dont elle a besoin pour satisfaire la requête, il n'y a donc aucune raison de consulter les pages de la table elles-mêmes. L'utilisation de l'index entraîne moins de trafic disque car vous avez une densité de lignes plus élevée dans l'index que dans la table en général.

Ceci est une explication ondulée à la main d'une technique d'optimisation spécifique utilisée par certaines bases de données. Beaucoup ont plusieurs techniques d'optimisation et de réglage.

Au final, SELECT * est utile pour les requêtes dynamiques que vous devez taper à la main, je ne l'utiliserais jamais pour du "vrai code". L'identification de colonnes individuelles donne à la base de données plus d'informations qu'elle peut utiliser pour optimiser la requête, et vous donne un meilleur contrôle de votre code contre les changements de schéma, etc.


Will, j'ai décliné votre réponse, simplement parce que vous utilisez NOT NULL avec la CLE PRIMAIRE. Y a-t-il une bonne raison pour vous d'écrire de cette façon?
Apprenti

4

Je pense qu'il n'y a pas de réponse exacte à votre question, car vous vous interrogez sur les performances et la facilité de maintenance de vos applications. Select columnest plus performante select *, mais si vous développez un système d'objet orienté, vous aimerez l'utiliser object.propertieset vous pourrez avoir besoin de propriétés dans n'importe quelle partie des applications, alors vous devrez écrire plus de méthodes pour obtenir des propriétés dans des situations spéciales si vous ne le faites pas utiliser select *et remplir toutes les propriétés. Vos applications doivent avoir de bonnes performances en utilisant select *et dans certains cas, vous devrez utiliser la colonne de sélection pour améliorer les performances. Ensuite, vous aurez le meilleur de deux mondes, la possibilité d'écrire et de maintenir des applications et des performances lorsque vous avez besoin de performances.


4

La réponse acceptée ici est fausse. Je suis tombé sur cela lorsqu'une autre question a été fermée comme une copie de celle-ci (alors que j'écrivais encore ma réponse - grr - d'où le SQL ci-dessous fait référence à l'autre question).

Vous devez toujours utiliser l'attribut SELECT, l'attribut .... NOT SELECT *

C'est principalement pour les problèmes de performances.

SELECT nom FROM utilisateurs WHERE nom = 'John';

N'est pas un exemple très utile. Considérez plutôt:

SELECT telephone FROM users WHERE name='John';

S'il y a un index sur (nom, téléphone), la requête peut être résolue sans avoir à rechercher les valeurs pertinentes dans la table - il y a un index de couverture .

De plus, supposons que la table ait un BLOB contenant une image de l'utilisateur, et un CV téléchargé, et une feuille de calcul ... en utilisant SELECT * ramènera toutes ces informations dans les tampons du SGBD (forçant à sortir d'autres informations utiles du cache). Ensuite, tout sera envoyé au client en utilisant le temps disponible sur le réseau et la mémoire sur le client pour les données qui sont redondantes.

Cela peut également causer des problèmes fonctionnels si le client récupère les données sous forme de tableau énuméré (tel que mysql_fetch_array de PHP ($ x, MYSQL_NUM)). Peut-être que lorsque le code a été écrit, «téléphone» était la troisième colonne à être retournée par SELECT *, mais alors quelqu'un arrive et décide d'ajouter une adresse e-mail à la table, placée avant «téléphone». Le champ souhaité est maintenant déplacé vers la 4ème colonne.


2

Il y a des raisons de faire les choses de toute façon. J'utilise beaucoup SELECT * sur PostgreSQL car il y a beaucoup de choses que vous pouvez faire avec SELECT * dans PostgreSQL que vous ne pouvez pas faire avec une liste de colonnes explicite, en particulier dans les procédures stockées. De même dans Informix, SELECT * sur une arborescence de table héritée peut vous donner des lignes irrégulières alors qu'une liste de colonnes explicite ne le peut pas car des colonnes supplémentaires dans les tables enfants sont également renvoyées.

La principale raison pour laquelle je fais cela dans PostgreSQL est que cela garantit que j'obtiens un type bien formé spécifique à une table. Cela me permet de prendre les résultats et de les utiliser comme type de table dans PostgreSQL. Cela permet également d'avoir beaucoup plus d'options dans la requête qu'une liste de colonnes rigides.

D'autre part, une liste de colonnes rigide vous permet de vérifier au niveau de l'application que les schémas de base de données n'ont pas changé de certaines manières et cela peut être utile. (Je fais de telles vérifications à un autre niveau.)

En ce qui concerne les performances, j'ai tendance à utiliser des VIEWs et des procédures stockées renvoyant des types (puis une liste de colonnes à l'intérieur de la procédure stockée). Cela me donne le contrôle sur les types renvoyés.

Mais gardez à l'esprit que j'utilise SELECT * généralement sur une couche d'abstraction plutôt que sur des tables de base.


2

Référence tirée de cet article:

Sans SELECT *: lorsque vous utilisez «SELECT *» à ce moment-là, vous sélectionnez plus de colonnes dans la base de données et une partie de cette colonne peut ne pas être utilisée par votre application. Cela créera des coûts et une charge supplémentaires sur le système de base de données et plus de données circuleront sur le réseau.

Avec SELECT *: Si vous avez des exigences particulières et que vous avez créé un environnement dynamique lors de l'ajout ou de la suppression de colonne automatiquement géré par le code de l'application. Dans ce cas particulier, vous n'avez pas besoin de changer le code de l'application et de la base de données et cela affectera automatiquement l'environnement de production. Dans ce cas, vous pouvez utiliser «SELECT *».


0

Juste pour ajouter une nuance à la discussion que je ne vois pas ici: en termes d'E / S, si vous utilisez une base de données avec un stockage orienté colonnes, vous pouvez faire BEAUCOUP moins d'E / S si vous ne demandez que certaines Colonnes. Au fur et à mesure que nous passons aux SSD, les avantages peuvent être un peu plus petits par rapport au stockage orienté lignes, mais il n'y a a) que la lecture des blocs contenant des colonnes qui vous intéressent b) la compression, qui réduit généralement considérablement la taille des données sur le disque et donc la volume de données lues à partir du disque.

Si vous n'êtes pas familier avec le stockage orienté colonnes, une implémentation pour Postgres provient de Citus Data, une autre est Greenplum, un autre Paraccel, une autre (en gros) est Amazon Redshift. Pour MySQL, il y a Infobright, InfiniDB maintenant presque disparu. D'autres offres commerciales incluent Vertica de HP, Sybase IQ, Teradata ...


-1
select * from table1 INTERSECT  select * from table2

égal

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

Pourriez-vous s'il vous plaît formater votre code en le mettant en surbrillance et en appuyant sur Ctrl + K
WhatsThePoint
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.