web-dev-qa-db-fra.com

sélectionnez * vs sélectionnez la colonne

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

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

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

Si elle tire toujours un tuple, les frais généraux d'E/S sont identiques.

En même temps, il peut y avoir une consommation de mémoire pour supprimer les colonnes demandées du tuple, si celui-ci extrait un tuple.

Donc, si tel est le cas, sélectionnez une colonne aura plus de surcharge de mémoire que celle de sélectionner *

116
Neel Basu

Il tire toujours un tuple (sauf dans les cas où la table a été segmentée verticalement - divisée en morceaux de colonnes), donc, pour répondre à la question que vous avez posée, peu importe la performance. Cependant, pour de nombreuses autres raisons (ci-dessous), vous devez toujours sélectionner spécifiquement les colonnes de votre choix, par leur nom.

Il tire toujours un tuple, car (dans chaque SGBDR de 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 défini I/O Pages (par exemple, dans SQL Server, chaque page mesure 8 kilo-octets). Et chaque entrée/sortie en lecture ou en écriture se fait par page, c'est-à-dire que toute écriture ou lecture est une page complète de données.

En raison de cette contrainte structurelle sous-jacente, il s'ensuit que chaque ligne de données d'une base de données doit toujours figurer sur une et une seule page. Il ne peut pas couvrir plusieurs pages de données (à l'exception de choses spéciales telles que les blobs, où les données de blob réelles sont stockées dans des morceaux de page séparés, et la colonne de rangée de tableau réelle reçoit alors uniquement un pointeur ...). Mais ces exceptions ne sont que 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 particulières)
Même dans ces cas particuliers, généralement, la rangée de données de la table elle-même (qui contient le pointeur sur les données réelles du blob, ou autre) doit être stockée sur un seul IO Page ...

EXCEPTION. Le seul endroit où Select * est OK, est dans la sous-requête après un Exists ou Not Exists clause de prédicat, 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 des colonnes demandées est un sous-ensemble de celles stockées dans un index, le processeur de requête doit extraire every la colonne stockée dans cet index, et pas uniquement celles demandées, pour les mêmes raisons - TOUTES les E/S doivent être effectuées en pages et les données d'index sont stockées dans IO Pages comme les données de table. Donc, si vous définissez "Tuple" pour une page d'index comme ensemble de colonnes stockées dans l'index, la déclaration est toujours vraie.
et la déclaration est vraie sur le plan esthétique, car le fait est qu’elle récupère les données en fonction de ce qui est stocké dans la page d’E/S, et non de ce que vous demandez, et que cela soit vrai si vous accédez à la table de base I/O Page ou une page d’E/S d’index.

Pour d'autres raisons, ne pas utiliser Select *, voir Pourquoi SELECT * considéré comme nuisible? :

26
Charles Bretana

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

  • étant donné que vous ne donnez à votre base aucune indication quant à 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 prendra du temps - pas beaucoup dans une seule requête - mais elle s’ajoute avec le temps.

  • si vous n'avez besoin que de 2/3 des colonnes, vous sélectionnez 1/3 de trop de données à extraire du disque et à envoyer sur le réseau.

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

  • dans SQL Server (vous ne connaissez pas bien les autres bases de données), si vous avez besoin d'un sous-ensemble de colonnes, il est toujours possible qu'un index non clusterisé puisse couvrir cette demande (contient toutes les colonnes nécessaires). Avec un 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 des entrées/sorties sur disque et surcharge de mémoire. serait beaucoup moins comparé à faire un SELECT *.... requête.

Oui, il faut un peu plus de frappe au départ (des outils tels que Prompt SQL pour SQL Server vous y aideront même) - mais c’est vraiment un cas où il y a une règle sans exception: ne jamais utiliser SELECT * dans votre code de production. JAMAIS.

106
marc_s

Vous devriez toujours seulement select 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.

[edit]: Ce qui signifie accéder. Stupide cerveau encore en train de se réveiller.

20
Donnie

Sauf si vous stockez de gros blobs, la performance n'est pas un problème. La principale raison de ne pas utiliser SELECT * est que, si vous utilisez des lignes renvoyées sous forme de nuplets, les colonnes sont renvoyées dans l'ordre spécifié par le schéma, et si cela change, vous devrez corriger tout votre code.

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

7
gxti

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 n'avais pas SELECT cette colonne, sauf si je vraiment en avait besoin. Avoir ces données en suspens - particulièrement lorsque j'ai sélectionné plusieurs rangées - n'était qu'un problème.

Cependant, j'admettrai que, normalement, je demande toutes les colonnes d'une table.

6
Richard JP Le Guen

Lors d'une sélection SQL, la base de données fera toujours référence 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 cette information pour deux raisons. Un, pour simplement compiler la déclaration. Vous devez au minimum vous assurer de spécifier 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 de base de données sont mises en cache dans le système, mais le traitement doit encore être effectué.

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

Ce traitement n'est effectué que 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 dépendent énormément de la mise en cache des pages. De nombreuses bases de données modernes sont suffisamment petites pour tenir entièrement dans la 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 le vidage des pages.

Toutefois, si vous utilisez toujours le disque pour votre base de données, l’optimisation principale de nombreux systèmes consiste à utiliser les données des index plutôt que 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 client WHERE id = 1", il est fort probable que votre base de données extraira ces données de l'index plutôt que des tables.

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

Maintenant, la base de données contient toutes les données nécessaires pour satisfaire la requête. Il n’ya donc aucune raison de consulter les pages de la table elles-mêmes. L'utilisation de l'index entraîne moins de trafic sur le disque, car vous avez une densité de lignes plus élevée dans l'index par rapport à la table en général.

Il s’agit d’une explication manuelle 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.

En fin de compte, SELECT * est utile pour les requêtes dynamiques que vous devez taper à la main, je ne l’utiliserais jamais pour du "code réel". L'identification de colonnes individuelles fournit à la base de données davantage d'informations qu'elle peut utiliser pour optimiser la requête et vous permet de mieux contrôler votre code par rapport aux modifications de schéma, etc.

6
Will Hartung

La réponse acceptée ici est fausse. Je suis tombé sur cette information quand ne autre question a été clôturée comme une copie de celle-ci (alors que j'écrivais encore ma réponse - grr - le code SQL ci-dessous fait référence à l'autre question).

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

C'est principalement pour des problèmes de performance.

SELECT nom FROM utilisateurs WHERE name = 'John';

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

SELECT telephone FROM users WHERE name='John';

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

De plus, supposons que la table ait un BLOB contenant une image de l'utilisateur, un CV téléchargé et une feuille de calcul ... en utilisant SELECT * extraira toutes ces informations dans les mémoires tampons du SGBD (forçant d'autres informations utiles du cache). Ensuite, tout sera envoyé au client en utilisant le temps de disponibilité sur le réseau et la mémoire sur le client pour les données 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 ($ x, MYSQL_NUM)) de PHP. Peut-être que lorsque le code a été écrit "téléphone" était la troisième colonne à renvoyer par SELECT *, mais quelqu'un se présente et décide d'ajouter une adresse électronique à la table, placée avant "téléphone". Le champ souhaité est maintenant déplacé à la 4ème colonne.

4
symcbean

Je pense qu’il n’ya pas de réponse exacte à votre question, car vous avez des performances à régler et une facilité de maintenance de vos applications. Select column est plus performatique de select *, mais si vous développez un système d’objets orientés, vous aimerez utiliser object.properties et vous pouvez avoir besoin de propriétés dans n’importe quelle partie des applications, vous aurez alors besoin d’écrire plus de méthodes pour obtenir des propriétés dans des situations spéciales si vous n’utilisez pas select * et remplissez toutes les propriétés. Vos applications doivent avoir de bonnes performances avec select * et dans certains cas, vous aurez besoin de la colonne select pour améliorer les performances. Ensuite, vous aurez le meilleur de deux mondes: la facilité d’écriture et de maintenance des applications et la performance lorsque vous avez besoin de performance.

4
M.Torres

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 certaines de ces colonnes peuvent ne pas être utilisées. par votre application. Cela créera des coûts et une charge supplémentaires sur le système de base de données et augmentera le nombre de données acheminées sur le réseau.

Avec SELECT *: Si vous avez des exigences particulières et un environnement dynamique créé lors de l'ajout ou de la suppression d'une colonne, le code est automatiquement traité. Dans ce cas particulier, il n’est pas nécessaire de modifier le code de l’application et de la base de données, ce qui affectera automatiquement l’environnement de production. Dans ce cas, vous pouvez utiliser “SELECT *”.

2
Anvesh

Il y a des raisons pour 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 tables 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 le fais dans PostgreSQL est qu’elle me permet d’obtenir 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 à la requête de disposer de beaucoup plus d'options qu'une liste de colonnes rigide.

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, ce qui peut être utile. (Je fais ces vérifications à un autre niveau.)

En ce qui concerne les performances, j’ai tendance à utiliser les modes VIEW et procédures stockées renvoyant des types (puis une liste de colonnes dans la procédure stockée). Cela me donne le contrôle sur les types retournés.

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

2
Chris Travers

Juste pour ajouter une nuance à la discussion que je ne vois pas ici: Pour ce qui est des E/S, si vous utilisez une base de données avec stockage orienté colonne vous pouvez faire BEAUCOUP moins O si vous interrogez uniquement certaines colonnes. Au fur et à mesure que nous passons aux disques SSD, les avantages peuvent être un peu moins importants que le stockage orienté ligne mais il y a a) lire uniquement les blocs contenant des colonnes qui vous intéressent b) la compression, ce qui réduit généralement considérablement la taille des données sur le disque et donc la volume de données lues sur le disque.

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

0