Jointure de sélection SQL: est-il possible de préfixer toutes les colonnes en tant que «préfixe. *»?


206

Je me demande si cela est possible en SQL. Supposons que vous ayez deux tables A et B, que vous effectuez une sélection sur la table A et que vous vous joignez à la table B:

SELECT a.*, b.* FROM TABLE_A a JOIN TABLE_B b USING (some_id);

Si la table A contient les colonnes 'a_id', 'name' et 'some_id' et que la table B a 'b_id', 'name' et 'some_id', la requête renvoie les colonnes 'a_id', 'name', 'some_id ',' b_id ',' name ',' some_id '. Existe-t-il un moyen de préfixer les noms de colonne du tableau B sans répertorier chaque colonne individuellement? L'équivalent de ceci:

SELECT a.*, b.b_id as 'b.b_id', b.name as 'b.name', b.some_id as 'b.some_id'
FROM TABLE_A a JOIN TABLE_B b USING (some_id);

Mais, comme mentionné, sans répertorier chaque colonne, quelque chose comme:

SELECT a.*, b.* as 'b.*'
FROM TABLE_A a JOIN TABLE_B b USING (some_id);

Fondamentalement, quelque chose à dire, "préfixez chaque colonne retournée par b. * Avec" quelque chose "". Est-ce possible ou n'ai-je pas de chance?

Merci d'avance pour votre aide!

EDIT: des conseils pour ne pas utiliser SELECT * et ainsi de suite sont des conseils valides mais non pertinents dans mon contexte, veuillez donc vous en tenir au problème en cours - est-il possible d'ajouter un préfixe (une constante spécifiée dans la requête SQL) à tous les noms de colonne d'une table dans une jointure?

EDIT: mon objectif ultime est de pouvoir faire un SELECT * sur deux tables avec une jointure, et être capable de dire, à partir des noms des colonnes que j'obtiens dans mon jeu de résultats, quelles colonnes proviennent de la table A et quelles colonnes sont venues du tableau B. Encore une fois, je ne veux pas avoir à lister les colonnes individuellement, je dois pouvoir faire un SELECT *.


Que pensez-vous exactement du résultat de votre requête? Je suis confus
GregD

GregD: Je veux que tous les noms de colonnes qui sortent de b. * Soient préfixés avec une constante que je spécifie. Par exemple, au lieu de «nom» et «numéro», je veux spécifier, disons, le préfixe «spécial_» et obtenir «nom_ spécial» et «numéro_ spécial». Mais je ne veux pas faire cela pour chaque colonne individuellement.
foxdonut

6
Lorsque je fais un SELECT rapide pour voir les colonnes de plusieurs tables, je fais parfois SELECT 'AAAAA', A. *, 'BBBBB', B. * FROM TableA AS A JOIN TableB AS B ON A.ID = B.ID pour que je avoir au moins un identifiant de table lors de la numérisation le long des lignes
Kristen

Réponses:


35

Je vois deux situations possibles ici. Tout d'abord, vous voulez savoir s'il existe une norme SQL pour cela, que vous pouvez utiliser en général quelle que soit la base de données. Non, il n'y en a pas. Deuxièmement, vous voulez connaître un produit dbms spécifique. Ensuite, vous devez l'identifier. Mais j'imagine que la réponse la plus probable est que vous récupérerez quelque chose comme "a.id, b.id" car c'est ainsi que vous auriez besoin d'identifier les colonnes de votre expression SQL. Et le moyen le plus simple de découvrir la valeur par défaut est simplement de soumettre une telle requête et de voir ce que vous obtenez en retour. Si vous voulez spécifier quel préfixe vient avant le point, vous pouvez utiliser "SELECT * FROM a AS my_alias", par exemple.


11
Je ne sais pas comment cela répond à votre question. J'utilise MS SQL Server et j'ajoute un alias après que le nom de la table n'ajoute pas l'alias aux noms de colonnes dans l'ensemble de résultats.
paiego

74

Il semble que la réponse à votre question soit non, mais un hack que vous pouvez utiliser consiste à attribuer une colonne factice pour séparer chaque nouvelle table. Cela fonctionne particulièrement bien si vous parcourez un ensemble de résultats pour une liste de colonnes dans un langage de script tel que Python ou PHP.

SELECT '' as table1_dummy, table1.*, '' as table2_dummy, table2.*, '' as table3_dummy, table3.* FROM table1
JOIN table2 ON table2.table1id = table1.id
JOIN table3 ON table3.table1id = table1.id

Je sais que cela ne répond pas exactement à votre question, mais si vous êtes un codeur, c'est un excellent moyen de séparer les tableaux avec des noms de colonne en double. J'espère que cela aide quelqu'un.


24

Je comprends parfaitement pourquoi cela est nécessaire - au moins pour moi, c'est pratique lors du prototypage rapide quand il y a beaucoup de tables nécessaires à joindre, y compris de nombreuses jointures internes. Dès qu'un nom de colonne est le même dans un deuxième caractère générique de champ "jointable. *", Les valeurs de champ de la table principale sont remplacées par les valeurs de jointable. Sujet aux erreurs, frustrant et une violation de DRY lorsque vous devez spécifier manuellement les champs de la table avec des alias encore et encore ...

Voici une fonction PHP (Wordpress) pour y parvenir grâce à la génération de code avec un exemple d'utilisation. Dans l'exemple, il est utilisé pour générer rapidement une requête personnalisée qui fournira les champs d'une publication wordpress associée référencée via un champ de champs personnalisés avancé .

function prefixed_table_fields_wildcard($table, $alias)
{
    global $wpdb;
    $columns = $wpdb->get_results("SHOW COLUMNS FROM $table", ARRAY_A);

    $field_names = array();
    foreach ($columns as $column)
    {
        $field_names[] = $column["Field"];
    }
    $prefixed = array();
    foreach ($field_names as $field_name)
    {
        $prefixed[] = "`{$alias}`.`{$field_name}` AS `{$alias}.{$field_name}`";
    }

    return implode(", ", $prefixed);
}

function test_prefixed_table_fields_wildcard()
{
    global $wpdb;

    $query = "
    SELECT
        " . prefixed_table_fields_wildcard($wpdb->posts, 'campaigns') . ",
        " . prefixed_table_fields_wildcard($wpdb->posts, 'venues') . "
        FROM $wpdb->posts AS campaigns
    LEFT JOIN $wpdb->postmeta meta1 ON (meta1.meta_key = 'venue' AND campaigns.ID = meta1.post_id)
    LEFT JOIN $wpdb->posts venues ON (venues.post_status = 'publish' AND venues.post_type = 'venue' AND venues.ID = meta1.meta_value)
    WHERE 1
    AND campaigns.post_status = 'publish'
    AND campaigns.post_type = 'campaign'
    LIMIT 1
    ";

    echo "<pre>$query</pre>";

    $posts = $wpdb->get_results($query, OBJECT);

    echo "<pre>";
    print_r($posts);
    echo "</pre>";
}

Le résultat:

SELECT
    `campaigns`.`ID` AS `campaigns.ID`, `campaigns`.`post_author` AS `campaigns.post_author`, `campaigns`.`post_date` AS `campaigns.post_date`, `campaigns`.`post_date_gmt` AS `campaigns.post_date_gmt`, `campaigns`.`post_content` AS `campaigns.post_content`, `campaigns`.`post_title` AS `campaigns.post_title`, `campaigns`.`post_excerpt` AS `campaigns.post_excerpt`, `campaigns`.`post_status` AS `campaigns.post_status`, `campaigns`.`comment_status` AS `campaigns.comment_status`, `campaigns`.`ping_status` AS `campaigns.ping_status`, `campaigns`.`post_password` AS `campaigns.post_password`, `campaigns`.`post_name` AS `campaigns.post_name`, `campaigns`.`to_ping` AS `campaigns.to_ping`, `campaigns`.`pinged` AS `campaigns.pinged`, `campaigns`.`post_modified` AS `campaigns.post_modified`, `campaigns`.`post_modified_gmt` AS `campaigns.post_modified_gmt`, `campaigns`.`post_content_filtered` AS `campaigns.post_content_filtered`, `campaigns`.`post_parent` AS `campaigns.post_parent`, `campaigns`.`guid` AS `campaigns.guid`, `campaigns`.`menu_order` AS `campaigns.menu_order`, `campaigns`.`post_type` AS `campaigns.post_type`, `campaigns`.`post_mime_type` AS `campaigns.post_mime_type`, `campaigns`.`comment_count` AS `campaigns.comment_count`,
    `venues`.`ID` AS `venues.ID`, `venues`.`post_author` AS `venues.post_author`, `venues`.`post_date` AS `venues.post_date`, `venues`.`post_date_gmt` AS `venues.post_date_gmt`, `venues`.`post_content` AS `venues.post_content`, `venues`.`post_title` AS `venues.post_title`, `venues`.`post_excerpt` AS `venues.post_excerpt`, `venues`.`post_status` AS `venues.post_status`, `venues`.`comment_status` AS `venues.comment_status`, `venues`.`ping_status` AS `venues.ping_status`, `venues`.`post_password` AS `venues.post_password`, `venues`.`post_name` AS `venues.post_name`, `venues`.`to_ping` AS `venues.to_ping`, `venues`.`pinged` AS `venues.pinged`, `venues`.`post_modified` AS `venues.post_modified`, `venues`.`post_modified_gmt` AS `venues.post_modified_gmt`, `venues`.`post_content_filtered` AS `venues.post_content_filtered`, `venues`.`post_parent` AS `venues.post_parent`, `venues`.`guid` AS `venues.guid`, `venues`.`menu_order` AS `venues.menu_order`, `venues`.`post_type` AS `venues.post_type`, `venues`.`post_mime_type` AS `venues.post_mime_type`, `venues`.`comment_count` AS `venues.comment_count`
    FROM wp_posts AS campaigns
LEFT JOIN wp_postmeta meta1 ON (meta1.meta_key = 'venue' AND campaigns.ID = meta1.post_id)
LEFT JOIN wp_posts venues ON (venues.post_status = 'publish' AND venues.post_type = 'venue' AND venues.ID = meta1.meta_value)
WHERE 1
AND campaigns.post_status = 'publish'
AND campaigns.post_type = 'campaign'
LIMIT 1

Array
(
    [0] => stdClass Object
        (
            [campaigns.ID] => 33
            [campaigns.post_author] => 2
            [campaigns.post_date] => 2012-01-16 19:19:10
            [campaigns.post_date_gmt] => 2012-01-16 19:19:10
            [campaigns.post_content] => Lorem ipsum
            [campaigns.post_title] => Lorem ipsum
            [campaigns.post_excerpt] => 
            [campaigns.post_status] => publish
            [campaigns.comment_status] => closed
            [campaigns.ping_status] => closed
            [campaigns.post_password] => 
            [campaigns.post_name] => lorem-ipsum
            [campaigns.to_ping] => 
            [campaigns.pinged] => 
            [campaigns.post_modified] => 2012-01-16 21:01:55
            [campaigns.post_modified_gmt] => 2012-01-16 21:01:55
            [campaigns.post_content_filtered] => 
            [campaigns.post_parent] => 0
            [campaigns.guid] => http://example.com/?p=33
            [campaigns.menu_order] => 0
            [campaigns.post_type] => campaign
            [campaigns.post_mime_type] => 
            [campaigns.comment_count] => 0
            [venues.ID] => 84
            [venues.post_author] => 2
            [venues.post_date] => 2012-01-16 20:12:05
            [venues.post_date_gmt] => 2012-01-16 20:12:05
            [venues.post_content] => Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
            [venues.post_title] => Lorem ipsum venue
            [venues.post_excerpt] => 
            [venues.post_status] => publish
            [venues.comment_status] => closed
            [venues.ping_status] => closed
            [venues.post_password] => 
            [venues.post_name] => lorem-ipsum-venue
            [venues.to_ping] => 
            [venues.pinged] => 
            [venues.post_modified] => 2012-01-16 20:53:37
            [venues.post_modified_gmt] => 2012-01-16 20:53:37
            [venues.post_content_filtered] => 
            [venues.post_parent] => 0
            [venues.guid] => http://example.com/?p=84
            [venues.menu_order] => 0
            [venues.post_type] => venue
            [venues.post_mime_type] => 
            [venues.comment_count] => 0
        )
)

13

La seule base de données que je connais qui le fait est SQLite, selon les paramètres que vous configurez avec PRAGMA full_column_nameset PRAGMA short_column_names. Voir http://www.sqlite.org/pragma.html

Sinon, tout ce que je peux recommander est de récupérer les colonnes dans un jeu de résultats par position ordinale plutôt que par nom de colonne, si c'est trop difficile pour vous de taper les noms des colonnes dans votre requête.

Ceci est un bon exemple des raisons pour lesquelles c'est une mauvaise pratique d'utiliserSELECT * - car vous aurez finalement besoin de taper tous les noms de colonne de toute façon.

Je comprends la nécessité de prendre en charge les colonnes qui peuvent changer de nom ou de position, mais l'utilisation de caractères génériques rend cela plus difficile , pas plus facile.


2
Notez que les deux full_column_nameset short_column_namessont déconseillés dans SQLite.
isanae

6

Je suis en quelque sorte le même bateau que OP - j'ai des dizaines de champs de 3 tables différentes que je rejoins, dont certains ont le même nom (ie. Id, nom, etc.). Je ne veux pas lister chaque champ, donc ma solution était d'alias ces champs qui partageaient un nom et d'utiliser select * pour ceux qui ont un nom unique.

Par exemple :

table a: id, nom, champ1, champ2 ...

table b: id, nom, champ3, champ4 ...

sélectionnez a.id comme aID, a.name comme aName, a. *, b.id comme bID, b.name comme bName, b. * .....

Lorsque j'accède aux résultats, je nous donne les noms alias de ces champs et j'ignore les noms "d'origine".

Peut-être pas la meilleure solution mais ça marche pour moi .... j'utilise mysql


5

Différents produits de base de données vous donneront différentes réponses; mais vous vous préparez à vous blesser si vous allez très loin. Vous feriez bien mieux de choisir les colonnes que vous voulez et de leur donner vos propres alias afin que l'identité de chaque colonne soit claire et que vous puissiez les distinguer dans les résultats.


1
Point pris, mais mon objectif ici est quelque chose de très générique, donc ne pas être explicite n'est pas un problème. En fait, devoir être précis serait un problème.
foxdonut

Voir plus de soumissions ci-dessous. Peut utiliser use dot.notation, qui est probablement ce que vous obtiendrez par défaut?
dkretz

C'est important pour la lisibilité. J'espérais le faire maintenant parce que j'ai un processus CTE lniked. ex. CTE_A -> CTE_B -> CTE_C -> CTE_D -> select / insert Il n'est pas nécessaire de spécifier les colonnes que je veux jusqu'à ce que l'instruction de sélection finale et les performances ne soient pas prises en compte.
ThomasRones

5

Cette question est très utile dans la pratique. Il suffit de répertorier toutes les colonnes explicites de la programmation logicielle, où vous faites particulièrement attention à faire face à toutes les conditions.

Imaginez lors du débogage, ou essayez d'utiliser le SGBD comme outil bureautique quotidien, au lieu d'une implémentation modifiable de l'infrastructure sous-jacente abstraite d'un programmeur spécifique, nous devons coder beaucoup de SQL. Le scénario peut être trouvé partout, comme la conversion de base de données, la migration, l'administration, etc. La plupart de ces SQL ne seront exécutés qu'une seule fois et ne seront plus jamais utilisés, car le nom de chaque colonne n'est qu'une perte de temps. Et n'oubliez pas que l'invention de SQL n'est pas réservée aux seuls programmeurs.

Habituellement, je vais créer une vue utilitaire avec des noms de colonnes préfixés, voici la fonction en pl / pgsql, ce n'est pas facile mais vous pouvez la convertir dans d'autres langages de procédure.

-- Create alias-view for specific table.

create or replace function mkaview(schema varchar, tab varchar, prefix varchar)
    returns table(orig varchar, alias varchar) as $$
declare
    qtab varchar;
    qview varchar;
    qcol varchar;
    qacol varchar;
    v record;
    sql varchar;
    len int;
begin
    qtab := '"' || schema || '"."' || tab || '"';
    qview := '"' || schema || '"."av' || prefix || tab || '"';
    sql := 'create view ' || qview || ' as select';

    for v in select * from information_schema.columns
            where table_schema = schema and table_name = tab
    loop
        qcol := '"' || v.column_name || '"';
        qacol := '"' || prefix || v.column_name || '"';

        sql := sql || ' ' || qcol || ' as ' || qacol;
        sql := sql || ', ';

        return query select qcol::varchar, qacol::varchar;
    end loop;

    len := length(sql);
    sql := left(sql, len - 2); -- trim the trailing ', '.
    sql := sql || ' from ' || qtab;

    raise info 'Execute SQL: %', sql;
    execute sql;
end
$$ language plpgsql;

Exemples:

-- This will create a view "avp_person" with "p_" prefix to all column names.
select * from mkaview('public', 'person', 'p_');

select * from avp_person;

3

Je comprends parfaitement votre problème concernant les noms de champs dupliqués.

J'en avais aussi besoin jusqu'à ce que je code ma propre fonction pour le résoudre. Si vous utilisez PHP, vous pouvez l'utiliser ou coder le vôtre dans la langue que vous utilisez si vous disposez des fonctionnalités suivantes.

L'astuce ici est de mysql_field_table()renvoyer le nom de la table et mysql_field_name()le champ pour chaque ligne du résultat s'il est obtenu avecmysql_num_fields() afin que vous puissiez les mélanger dans un nouveau tableau.

Cela préfixe toutes les colonnes;)

Cordialement,

function mysql_rows_with_columns($query) {
    $result = mysql_query($query);
    if (!$result) return false; // mysql_error() could be used outside
    $fields = mysql_num_fields($result);
    $rows = array();
    while ($row = mysql_fetch_row($result)) { 
        $newRow = array();
        for ($i=0; $i<$fields; $i++) {
            $table = mysql_field_table($result, $i);
            $name = mysql_field_name($result, $i);
            $newRow[$table . "." . $name] = $row[$i];
        }
        $rows[] = $newRow;
    }
    mysql_free_result($result);
    return $rows;
}

2

Il n'y a pas de standard SQL pour cela.

Cependant, avec la génération de code (soit à la demande lorsque les tables sont créées ou modifiées ou au moment de l'exécution), vous pouvez le faire assez facilement:

CREATE TABLE [dbo].[stackoverflow_329931_a](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [col2] [nchar](10) NULL,
    [col3] [nchar](10) NULL,
    [col4] [nchar](10) NULL,
 CONSTRAINT [PK_stackoverflow_329931_a] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE TABLE [dbo].[stackoverflow_329931_b](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [col2] [nchar](10) NULL,
    [col3] [nchar](10) NULL,
    [col4] [nchar](10) NULL,
 CONSTRAINT [PK_stackoverflow_329931_b] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

DECLARE @table1_name AS varchar(255)
DECLARE @table1_prefix AS varchar(255)
DECLARE @table2_name AS varchar(255)
DECLARE @table2_prefix AS varchar(255)
DECLARE @join_condition AS varchar(255)
SET @table1_name = 'stackoverflow_329931_a'
SET @table1_prefix = 'a_'
SET @table2_name = 'stackoverflow_329931_b'
SET @table2_prefix = 'b_'
SET @join_condition = 'a.[id] = b.[id]'

DECLARE @CRLF AS varchar(2)
SET @CRLF = CHAR(13) + CHAR(10)

DECLARE @a_columnlist AS varchar(MAX)
DECLARE @b_columnlist AS varchar(MAX)
DECLARE @sql AS varchar(MAX)

SELECT @a_columnlist = COALESCE(@a_columnlist + @CRLF + ',', '') + 'a.[' + COLUMN_NAME + '] AS [' + @table1_prefix + COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @table1_name
ORDER BY ORDINAL_POSITION

SELECT @b_columnlist = COALESCE(@b_columnlist + @CRLF + ',', '') + 'b.[' + COLUMN_NAME + '] AS [' + @table2_prefix + COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @table2_name
ORDER BY ORDINAL_POSITION

SET @sql = 'SELECT ' + @a_columnlist + '
,' + @b_columnlist + '
FROM [' + @table1_name + '] AS a
INNER JOIN [' + @table2_name + '] AS b
ON (' + @join_condition + ')'

PRINT @sql
-- EXEC (@sql)

cela fonctionnerait mais la question est plutôt idiote. pourquoi ne pas simplement effectuer une union ou une sous-requête. Pourquoi voudriez-vous rejoindre et toujours vouloir des préfixes de table dans les noms de colonne?
D3vtr0n

Cade: merci pour l'info, c'est intéressant. Malheureusement, la génération / modification de la base de données n'est pas une option dans mon cas. Devtron: si vous essayez de mapper les informations qui reviennent d'une requête à différentes propriétés d'un objet, ces informations deviennent très utiles.
foxdonut

1
Parfois, les noms de colonne dans différentes tables sont identiques, mais ne contiennent pas les mêmes valeurs. D'où la nécessité de les préfixer pour les distinguer dans les vues ou les tables dérivées (qui doivent avoir tous les noms de colonnes uniques).
Cade Roux

@Frederic, votre code doit vivre quelque part - cela ne fait que générer le code. Encore une fois, cela peut être fait une fois pendant le développement ou dynamiquement au moment de l'exécution.
Cade Roux

1

Il y a deux façons dont je peux penser pour que cela se produise de manière réutilisable. L'une consiste à renommer toutes vos colonnes avec un préfixe pour la table dont elles proviennent. Je l'ai vu plusieurs fois, mais je n'aime vraiment pas ça. Je trouve que c'est redondant, cause beaucoup de dactylographie et vous pouvez toujours utiliser des alias lorsque vous avez besoin de couvrir le cas d'un nom de colonne dont l'origine n'est pas claire.

L'autre façon, que je vous recommanderais de faire dans votre situation si vous vous engagez à le voir, est de créer des vues pour chaque table qui alias les noms de table. Ensuite, vous vous associez à ces vues, plutôt qu'aux tables. De cette façon, vous êtes libre d'utiliser * si vous le souhaitez, libre d'utiliser les tables d'origine avec les noms de colonnes d'origine si vous le souhaitez, et cela facilite également l'écriture de toutes les requêtes suivantes, car vous avez déjà effectué le changement de nom dans les vues.

Enfin, je ne comprends pas pourquoi vous devez savoir de quelle table provient chacune des colonnes. Est-ce important? En fin de compte, ce qui importe, ce sont les données qu'elles contiennent. Que UserID provienne de la table User ou de la table UserQuestion n'a pas vraiment d'importance. Cela importe, bien sûr, lorsque vous devez le mettre à jour, mais à ce stade, vous devez déjà bien connaître votre schéma pour le déterminer.


"Enfin, je ne comprends pas pourquoi vous devez savoir de quelle table provient chacune des colonnes. Est-ce important?" <- 11 ans plus tard, un cas d'utilisation est la numérisation de structures dans Go.
Lee Benson

1

Ou vous pouvez utiliser Red Gate SQL Refactor ou SQL Prompt, qui étend votre SELECT * en listes de colonnes en cliquant sur le bouton Tab

donc dans votre cas, si vous tapez SELECT * FROM A JOIN B ... Allez à la fin de *, bouton Tab, le tour est joué! vous verrez SELECT A.column1, A.column2, ...., B.column1, B.column2 FROM A JOIN B

Ce n'est pas gratuit cependant


1

Impossible de le faire sans alias, simplement parce que, comment allez-vous référencer un champ dans la clause where, si ce champ existe dans les 2 ou 3 tables que vous joignez? Il ne sera pas clair pour mysql lequel vous essayez de référencer.


1

J'ai résolu un problème similaire en renommant les champs dans les tables impliquées. Oui, j'ai eu le privilège de le faire et je comprends que tout le monde peut ne pas l'avoir. J'ai ajouté un préfixe à chaque champ d'une table représentant le nom de la table. Ainsi, le SQL affiché par OP resterait inchangé -

SELECT a.*, b.* FROM TABLE_A a JOIN TABLE_B b USING (some_id);

et donne toujours les résultats attendus - facilité d'identifier à quelle table les champs de sortie appartiennent.


0

select * entraîne généralement un mauvais code, car de nouvelles colonnes ont tendance à être ajoutées ou l'ordre des colonnes change dans les tableaux assez fréquemment, ce qui brise généralement select * de manière très subtile. La liste des colonnes est donc la bonne solution.

Quant à la façon de faire votre requête, vous n'êtes pas sûr de mysql, mais dans sqlserver, vous pouvez sélectionner les noms de colonnes dans syscolumns et créer dynamiquement la clause select.


Point pris, mais dans mon contexte, j'ai besoin de quelque chose de générique et dynamique, donc en fait mon code s'adaptera aux nouvelles colonnes ajoutées / réorganisées / etc. Je ne veux pas avoir à répertorier les colonnes individuellement.
foxdonut

5
Choisir parmi syscolumns pour construire dynamiquement une instruction select est un terrible hack, et je ne le recommanderais pas en production.
Juliet

0

Si vous êtes préoccupé par les modifications de schéma, cela peut fonctionner pour vous: 1. Exécutez une requête 'DESCRIBE table' sur toutes les tables impliquées. 2. Utilisez les noms de champ renvoyés pour construire dynamiquement une chaîne de noms de colonne préfixée avec votre alias choisi.


0

Il y a une réponse directe à votre question pour ceux qui utilisent la MySQL C-API.

Étant donné le SQL:

  SELECT a.*, b.*, c.* FROM table_a a JOIN table_b b USING (x) JOIN table_c c USING (y)

Les résultats de 'mysql_stmt_result_metadata ()' donnent la définition de vos champs à partir de votre requête SQL préparée dans la structure MYSQL_FIELD []. Chaque champ contient les données suivantes:

  char *name;                 /* Name of column (may be the alias) */
  char *org_name;             /* Original column name, if an alias */
  char *table;                /* Table of column if column was a field */
  char *org_table;            /* Org table name, if table was an alias */
  char *db;                   /* Database for table */
  char *catalog;              /* Catalog for table */
  char *def;                  /* Default value (set by mysql_list_fields) */
  unsigned long length;       /* Width of column (create length) */
  unsigned long max_length;   /* Max width for selected set */
  unsigned int name_length;
  unsigned int org_name_length;
  unsigned int table_length;
  unsigned int org_table_length;
  unsigned int db_length;
  unsigned int catalog_length;
  unsigned int def_length;
  unsigned int flags;         /* Div flags */
  unsigned int decimals;      /* Number of decimals in field */
  unsigned int charsetnr;     /* Character set */
  enum enum_field_types type; /* Type of field. See mysql_com.h for types */

Notez les champs: catalogue, table, org_name

Vous savez maintenant quels champs de votre SQL appartiennent à quel schéma (ou catalogue) et table. Cela suffit pour identifier génériquement chaque champ à partir d'une requête SQL multi-tables, sans avoir à alias quoi que ce soit.

Un produit réel SqlYOG est montré pour utiliser ces données exactes dans un tel manoir qu'ils sont capables de mettre à jour indépendamment chaque table d'une jointure multi-table, lorsque les champs PK sont présents.


0

À partir de cette solution , voici comment j'aborderais le problème:

Créez d'abord une liste de toutes les ASinstructions:

DECLARE @asStatements varchar(8000)

SELECT @asStatements = ISNULL(@asStatements + ', ','') + QUOTENAME(table_name) + '.' + QUOTENAME(column_name) + ' AS ' + '[' + table_name + '.' + column_name + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TABLE_A' OR TABLE_NAME = 'TABLE_B'
ORDER BY ORDINAL_POSITION

Ensuite, utilisez-le dans votre requête:

EXEC('SELECT ' + @asStatements + ' FROM TABLE_A a JOIN TABLE_B b USING (some_id)');

Cependant, cela peut nécessiter des modifications car quelque chose de similaire n'est testé que dans SQL Server. Mais ce code ne fonctionne pas exactement dans SQL Server car USING n'est pas pris en charge.

Veuillez commenter si vous pouvez tester / corriger ce code, par exemple pour MySQL.


0

J'ai récemment rencontré ce problème dans NodeJS et Postgres.

Approche ES6

Je ne connais aucune fonctionnalité SGBDR qui offre cette fonctionnalité, j'ai donc créé un objet contenant tous mes champs, par exemple:

const schema = { columns: ['id','another_column','yet_another_column'] }

Défini un réducteur pour concaténer les chaînes avec un nom de table:

const prefix = (table, columns) => columns.reduce((previous, column) => {
  previous.push(table + '.' + column + ' AS ' + table + '_' + column);
  return previous;
}, []);

Cela renvoie un tableau de chaînes. Appelez-le pour chaque tableau et combinez les résultats:

const columns_joined = [...prefix('tab1',schema.columns), ...prefix('tab2',schema.columns)];

Générez la dernière instruction SQL:

console.log('SELECT ' + columns_joined.join(',') + ' FROM tab1, tab2 WHERE tab1.id = tab2.id');

En aucune façon! C'est une injection SQL hacky et ne fonctionne pas avec les expressions.
ratijas

0

J'ai implémenté une solution basée sur la réponse suggérant d'utiliser des colonnes factices ou sentinelles dans le nœud. Vous l'utiliseriez en générant du SQL comme:

select 
    s.*
  , '' as _prefix__creator_
  , u.*
  , '' as _prefix__speaker_
  , p.*
from statements s 
  left join users u on s.creator_user_id = u.user_id
  left join persons p on s.speaker_person_id = p.person_id

Et puis post-traiter la ligne que vous récupérez à partir de votre pilote de base de données comme addPrefixes(row).

Implémentation (basée sur le fields/ rowsretourné par mon pilote, mais devrait être facile à changer pour les autres pilotes de base de données):

const PREFIX_INDICATOR = '_prefix__'
const STOP_PREFIX_INDICATOR = '_stop_prefix'

/** Adds a <prefix> to all properties that follow a property with the name: PREFIX_INDICATOR<prefix> */
function addPrefixes(fields, row) {
  let prefix = null
  for (const field of fields) {
    const key = field.name
    if (key.startsWith(PREFIX_INDICATOR)) {
      if (row[key] !== '') {
        throw new Error(`PREFIX_INDICATOR ${PREFIX_INDICATOR} must not appear with a value, but had value: ${row[key]}`)
      }
      prefix = key.substr(PREFIX_INDICATOR.length)
      delete row[key]
    } else if (key === STOP_PREFIX_INDICATOR) {
      if (row[key] !== '') {
        throw new Error(`STOP_PREFIX_INDICATOR ${STOP_PREFIX_INDICATOR} must not appear with a value, but had value: ${row[key]}`)
      }
      prefix = null
      delete row[key]
    } else if (prefix) {
      const prefixedKey = prefix + key
      row[prefixedKey] = row[key]
      delete row[key]
    }
  }
  return row
}

Tester:

const {
  addPrefixes,
  PREFIX_INDICATOR,
  STOP_PREFIX_INDICATOR,
} = require('./BaseDao')

describe('addPrefixes', () => {
  test('adds prefixes', () => {
    const fields = [
      {name: 'id'},
      {name: PREFIX_INDICATOR + 'my_prefix_'},
      {name: 'foo'},
      {name: STOP_PREFIX_INDICATOR},
      {name: 'baz'},
    ]
    const row = {
      id: 1,
      [PREFIX_INDICATOR + 'my_prefix_']: '',
      foo: 'bar',
      [STOP_PREFIX_INDICATOR]: '',
      baz: 'spaz'
    }
    const expected = {
      id: 1,
      my_prefix_foo: 'bar',
      baz: 'spaz',
    }
    expect(addPrefixes(fields, row)).toEqual(expected)
  })
})

0

Ce que je fais, c'est utiliser Excel pour concaténer la procédure. Par exemple, je sélectionne d'abord * et récupère toutes les colonnes, les colle dans Excel. Ensuite, écrivez le code dont j'ai besoin pour entourer la colonne. Supposons que je devais publier un aperçu de plusieurs colonnes. J'aurais mes champs dans la colonne a et "as prev_" dans la colonne B et mes champs à nouveau dans la colonne c. Dans la colonne d, j'aurais une colonne.

Utilisez ensuite concatanate dans la colonne e et fusionnez-les ensemble, en veillant à inclure des espaces. Ensuite, coupez et collez cela dans votre code SQL. J'ai également utilisé cette méthode pour faire des déclarations de cas pour le même champ et d'autres codes plus longs que je dois faire pour chaque champ dans une table de plusieurs centaines de champs.


0

En postgres, j'utilise les fonctions json pour renvoyer à la place des objets json .... puis, après une requête, je json_decode les champs avec un suffixe _json.

C'EST À DIRE:

select row_to_json(tab1.*),tab1_json, row_to_json(tab2.*) tab2_json 
 from tab1
 join tab2 on tab2.t1id=tab1.id

puis en PHP (ou tout autre langage), je fais une boucle dans les colonnes retournées et json_decode () si elles ont le suffixe "_json" (en supprimant également le suffixe. Au final, j'obtiens un objet appelé "tab1" qui inclut tous les champs tab1 et un autre appelé "tab2" qui inclut tous les champs tab2.


-1

PHP 7.2 + MySQL / Mariadb

MySQL vous enverra plusieurs champs du même nom. Même dans le client terminal. Mais si vous voulez un tableau associatif, vous devrez créer les clés vous-même.

Merci à @axelbrz pour l'original. Je l'ai porté sur un php plus récent et l'ai nettoyé un peu:

function mysqli_rows_with_columns($link, $query) {
    $result = mysqli_query($link, $query);
    if (!$result) {
        return mysqli_error($link);
    }
    $field_count = mysqli_num_fields($result);
    $fields = array();
    for ($i = 0; $i < $field_count; $i++) {
        $field = mysqli_fetch_field_direct($result, $i);
        $fields[] = $field->table . '.' . $field->name; # changed by AS
        #$fields[] = $field->orgtable . '.' . $field->orgname; # actual table/field names
    }
    $rows = array();
    while ($row = mysqli_fetch_row($result)) {
        $new_row = array();
        for ($i = 0; $i < $field_count; $i++) {
            $new_row[$fields[$i]] = $row[$i];
        }
        $rows[] = $new_row;
    }
    mysqli_free_result($result);
    return $rows;
}

$link = mysqli_connect('localhost', 'fixme', 'fixme', 'fixme');
print_r(mysqli_rows_with_columns($link, 'select foo.*, bar.* from foo, bar'));
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.