Une sous-requête est une instruction SELECT imbriquée dans une autre instruction SQL. Utilisez les sous-requêtes pour filtrer des lignes par rapport à un ensemble dynamique de valeurs, vérifier l'existence d'enregistrements associés, calculer des valeurs agrégées directement ou créer des tables dérivées pour des transformations complexes.
MaxCompute prend en charge six types de sous-requêtes : sous-requête de base, IN SUBQUERY, NOT IN SUBQUERY, EXISTS SUBQUERY, NOT EXISTS SUBQUERY et SCALAR SUBQUERY. Certains types sont automatiquement convertis en opérations JOIN lors de l'exécution. Comprendre quand cette conversion se produit — et quand elle ne se produit pas — vous aide à rédiger des requêtes efficaces et à éviter des résultats inattendus.
Concepts clés
Deux distinctions permettent de clarifier la plupart des différences de comportement entre les types de sous-requêtes.
Sous-requêtes corrélées et non corrélées
Une sous-requête corrélée fait référence à une ou plusieurs colonnes de la requête externe. MaxCompute l'évalue pour chaque ligne de la requête externe, en utilisant la valeur de la colonne externe comme filtre.
Une sous-requête non corrélée ne fait aucune référence à la requête externe. MaxCompute l'évalue une seule fois et réutilise le résultat.
Sous-requêtes scalaires et non scalaires
Une sous-requête scalaire renvoie exactement une ligne et une colonne. Le résultat peut être utilisé comme expression de valeur — dans une comparaison de clause
WHEREou dans une listeSELECT.Une sous-requête non scalaire renvoie zéro ou plusieurs lignes. Elle est utilisée avec des opérateurs tels que
IN,NOT IN,EXISTSouNOT EXISTS.
Conversion JOIN et indice MAPJOIN
MaxCompute convertit les sous-requêtes SCALAR, IN, NOT IN, EXISTS et NOT EXISTS en opérations JOIN lorsque cela est possible. Si le résultat de la sous-requête est petit, ajoutez un indice MAPJOIN pour forcer l'algorithme JOIN par diffusion (broadcast) :
SELECT /*+ MAPJOIN(b) */ *
FROM sale_detail a
WHERE a.total_price IN (SELECT total_price FROM shop b);
Données d'exemple
Les exemples de cette rubrique utilisent les tables suivantes. Exécutez ces instructions pour configurer les données d'exemple :
-- Create a partitioned table named sale_detail.
CREATE TABLE IF NOT EXISTS sale_detail
(
shop_name STRING,
customer_id STRING,
total_price DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);
-- Add partitions.
ALTER TABLE sale_detail ADD PARTITION (sale_date='2013', region='china')
PARTITION (sale_date='2014', region='shanghai');
-- Insert sample data.
INSERT INTO sale_detail PARTITION (sale_date='2013', region='china')
VALUES ('s1','c1',100.1),('s2','c2',100.2),('s3','c3',100.3);
INSERT INTO sale_detail PARTITION (sale_date='2014', region='shanghai')
VALUES ('null','c5',null),('s6','c6',100.4),('s7','c7',100.5);
Interrogez la table pour vérifier les données :
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail;
Résultat :
+------------+-------------+-------------+------------+------------+
| shop_name | customer_id | total_price | sale_date | region |
+------------+-------------+-------------+------------+------------+
| s1 | c1 | 100.1 | 2013 | china |
| s2 | c2 | 100.2 | 2013 | china |
| s3 | c3 | 100.3 | 2013 | china |
| null | c5 | NULL | 2014 | shanghai |
| s6 | c6 | 100.4 | 2014 | shanghai |
| s7 | c7 | 100.5 | 2014 | shanghai |
+------------+-------------+-------------+------------+------------+
Sous-requête de base
Une sous-requête de base apparaît dans la clause FROM et agit comme une table dérivée. Vous pouvez la joindre à d'autres tables ou sous-requêtes comme n'importe quelle table régulière. Pour plus d'informations sur la syntaxe JOIN, consultez la section JOIN.
Syntaxe 1 — sous-requête en tant que table dérivée :
SELECT <select_expr> FROM (<select_statement>) [<sq_alias_name>];
Syntaxe 2 — sous-requête dans la liste de sélection (le résultat doit être une seule ligne) :
SELECT (<select_statement>) FROM <table_name>;
Paramètres
| Paramètre | Obligatoire | Description |
|---|---|---|
select_expr |
Oui | Colonnes ou expressions à renvoyer, au format col1_name, col2_name, expression, ... |
select_statement |
Oui | La sous-requête. Pour la Syntaxe 2, la sous-requête doit renvoyer exactement une ligne. Consultez la section Syntaxe SELECT. |
sq_alias_name |
Non | Un alias pour la sous-requête |
table_name |
Oui (Syntaxe 2) | La table externe à interroger |
Exemples
Exemple 1 — Syntaxe 1, sous-requête en tant que table dérivée :
SET odps.sql.allow.fullscan=true;
SELECT * FROM (SELECT shop_name FROM sale_detail) a;
Résultat :
+------------+
| shop_name |
+------------+
| s1 |
| s2 |
| s3 |
| null |
| s6 |
| s7 |
+------------+
Exemple 2 — Syntaxe 2, sous-requête à ligne unique dans la liste de sélection :
SET odps.sql.allow.fullscan=true;
SELECT (SELECT * FROM sale_detail WHERE shop_name='s1') FROM sale_detail;
Résultat :
+------------+-------------+-------------+------------+------------+
| shop_name | customer_id | total_price | sale_date | region |
+------------+-------------+-------------+------------+------------+
| s1 | c1 | 100.1 | 2013 | china |
| s1 | c1 | 100.1 | 2013 | china |
| s1 | c1 | 100.1 | 2013 | china |
| s1 | c1 | 100.1 | 2013 | china |
| s1 | c1 | 100.1 | 2013 | china |
| s1 | c1 | 100.1 | 2013 | china |
+------------+-------------+-------------+------------+------------+
Exemple 3 — Syntaxe 1, sous-requête jointe à une autre table :
-- Create a shop table, then join it with sale_detail via a subquery.
CREATE TABLE shop AS SELECT shop_name, customer_id, total_price FROM sale_detail;
SELECT a.shop_name, a.customer_id, a.total_price
FROM (SELECT * FROM shop) a
JOIN sale_detail ON a.shop_name = sale_detail.shop_name;
Résultat :
+------------+-------------+-------------+
| shop_name | customer_id | total_price |
+------------+-------------+-------------+
| null | c5 | NULL |
| s6 | c6 | 100.4 |
| s7 | c7 | 100.5 |
| s1 | c1 | 100.1 |
| s2 | c2 | 100.2 |
| s3 | c3 | 100.3 |
+------------+-------------+-------------+
IN SUBQUERY
La clause IN SUBQUERY filtre la requête externe pour ne conserver que les lignes dont la valeur d'une colonne correspond à l'une des valeurs renvoyées par la sous-requête. MaxCompute convertit IN SUBQUERY en LEFT SEMI JOIN lorsque la sous-requête est utilisée comme condition de jointure WHERE. Pour plus d'informations sur LEFT SEMI JOIN, consultez la section JOIN.
Syntaxe 1 — non corrélée :
SELECT <select_expr1> FROM <table_name1>
WHERE <select_expr2> IN (SELECT <select_expr3> FROM <table_name2>);
-- Equivalent LEFT SEMI JOIN:
SELECT <select_expr1> FROM <table_name1> <alias_name1>
LEFT SEMI JOIN <table_name2> <alias_name2>
ON <alias_name1>.<select_expr2> = <alias_name2>.<select_expr3>;
Siselect_expr2fait référence à une colonne de clé de partition, MaxCompute exécute la sous-requête en tant que tâche distincte au lieu de la convertir en LEFT SEMI JOIN. MaxCompute utilise ensuite les résultats de la sous-requête pour appliquer l'élagage des partitions surtable_name1, de sorte que seules les partitions correspondantes sont lues.
Syntaxe 2 — corrélée (fait référence à une colonne externe) :
SELECT <select_expr1> FROM <table_name1>
WHERE <select_expr2> IN (
SELECT <select_expr3> FROM <table_name2>
WHERE <table_name1>.<col_name> = <table_name2>.<col_name>
);
MaxCompute V2.0 prend en charge les expressions qui font référence aux colonnes de la sous-requête et de la requête externe. Ces conditions corrélées deviennent partie intégrante de la condition ON dans l'opération SEMI JOIN. MaxCompute V1.0 ne prend pas en charge ces expressions.
Lorsque IN SUBQUERY ne peut pas être convertie en condition de jointure — par exemple, lorsqu'elle apparaît en dehors d'une clauseWHEREou lorsque la conditionWHEREne peut pas être exprimée sous forme de jointure — MaxCompute l'exécute en tant que tâche distincte. Les conditions corrélées ne sont pas prises en charge dans ce cas.
Syntax 3 — sous-requête multi-colonnes :
Utilisez plusieurs colonnes dans l'expression IN pour faire correspondre les résultats d'une sous-requête qui renvoie plusieurs colonnes. Cela élimine une opération JOIN par rapport à la division de la requête en sous-requêtes séparées. Les formes suivantes sont prises en charge :
Une instruction
SELECTsimple renvoyant plusieurs colonnesDes fonctions d'agrégation dans la sous-requête
Des tuples constants
-- Multiple columns with a simple SELECT:
SELECT a, b FROM t1 WHERE (c, d) IN (SELECT a, b FROM t2 WHERE e = t1.e);
-- Multiple columns with aggregate functions:
SELECT a, b FROM t1 WHERE (c, d) IN (SELECT MAX(a), b FROM t2 WHERE e = t1.e GROUP BY b HAVING MAX(a) > 0);
-- Constant tuples:
SELECT a, b FROM t1 WHERE (c, d) IN ((1, 3), (1, 1));
Paramètres
| Paramètre | Obligatoire | Description |
|---|---|---|
select_expr1 |
Oui | Colonnes à renvoyer depuis la requête externe |
table_name1, table_name2 |
Oui | Les noms des tables externe et interne |
select_expr2, select_expr3 |
Oui | Colonnes à comparer ; les valeurs sont mises en correspondance positionnellement |
col_name |
Oui (Syntaxe 2) | Colonne utilisée comme condition corrélée |
Remarque d'utilisation
Les valeurs NULL dans le résultat de la sous-requête sont automatiquement exclues de la comparaison.
Exemples
Exemple 1 — Syntaxe 1 :
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail WHERE total_price IN (SELECT total_price FROM shop);
Résultat :
+-----------+-------------+-------------+-----------+----------+
| shop_name | customer_id | total_price | sale_date | region |
+-----------+-------------+-------------+-----------+----------+
| s1 | c1 | 100.1 | 2013 | china |
| s2 | c2 | 100.2 | 2013 | china |
| s3 | c3 | 100.3 | 2013 | china |
| s6 | c6 | 100.4 | 2014 | shanghai |
| s7 | c7 | 100.5 | 2014 | shanghai |
+-----------+-------------+-------------+-----------+----------+
Exemple 2 — Syntaxe 2, condition corrélée :
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail
WHERE total_price IN (
SELECT total_price FROM shop
WHERE customer_id = shop.customer_id
);
Résultat :
+-----------+-------------+-------------+-----------+----------+
| shop_name | customer_id | total_price | sale_date | region |
+-----------+-------------+-------------+-----------+----------+
| s1 | c1 | 100.1 | 2013 | china |
| s2 | c2 | 100.2 | 2013 | china |
| s3 | c3 | 100.3 | 2013 | china |
| s6 | c6 | 100.4 | 2014 | shanghai |
| s7 | c7 | 100.5 | 2014 | shanghai |
+-----------+-------------+-------------+-----------+----------+
Exemple 3 — Syntaxe 3, sous-requête multi-colonnes :
-- Set up sample tables.
CREATE TABLE IF NOT EXISTS t1(a BIGINT, b BIGINT, c BIGINT, d BIGINT, e BIGINT);
CREATE TABLE IF NOT EXISTS t2(a BIGINT, b BIGINT, c BIGINT, d BIGINT, e BIGINT);
INSERT INTO TABLE t1 VALUES (1,3,2,1,1),(2,2,1,3,1),(3,1,1,1,1),(2,1,1,0,1),(1,1,1,0,1);
INSERT INTO TABLE t2 VALUES (1,3,5,0,1),(2,2,3,1,1),(3,1,1,0,1),(2,1,1,0,1),(1,1,1,0,1);
-- Scenario 1: Simple SELECT with multiple columns.
SELECT a, b FROM t1 WHERE (c, d) IN (SELECT a, b FROM t2 WHERE e = t1.e);
-- Result:
-- +------------+------------+
-- | a | b |
-- +------------+------------+
-- | 1 | 3 |
-- | 2 | 2 |
-- | 3 | 1 |
-- +------------+------------+
-- Scenario 2: Aggregate functions.
SELECT a, b FROM t1 WHERE (c, d) IN (SELECT MAX(a), b FROM t2 WHERE e = t1.e GROUP BY b HAVING MAX(a) > 0);
-- Result:
-- +------------+------------+
-- | a | b |
-- +------------+------------+
-- | 2 | 2 |
-- +------------+------------+
-- Scenario 3: Constant tuples.
SELECT a, b FROM t1 WHERE (c, d) IN ((1, 3), (1, 1));
-- Result:
-- +------------+------------+
-- | a | b |
-- +------------+------------+
-- | 2 | 2 |
-- | 3 | 1 |
-- +------------+------------+
NOT IN SUBQUERY
La clause NOT IN SUBQUERY filtre la requête externe pour ne conserver que les lignes dont la valeur d'une colonne ne correspond à aucune valeur renvoyée par la sous-requête. MaxCompute convertit NOT IN SUBQUERY en LEFT ANTI JOIN lorsqu'elle est utilisée comme condition de jointure WHERE. Pour plus d'informations sur LEFT ANTI JOIN, consultez la section JOIN.
Si une ligne de la table externe contient une valeur NULL dans la colonne comparée, l'expression NOT IN prend la valeur NULL pour cette ligne, et la condition WHERE échoue. Aucune ligne n'est renvoyée dans ce cas. Ce comportement diffère de celui de LEFT ANTI JOIN, qui gère les valeurs NULL différemment.
Syntaxe 1 — non corrélée :
SELECT <select_expr1> FROM <table_name1>
WHERE <select_expr2> NOT IN (SELECT <select_expr2> FROM <table_name2>);
-- Equivalent LEFT ANTI JOIN:
SELECT <select_expr1> FROM <table_name1> <alias_name1>
LEFT ANTI JOIN <table_name2> <alias_name2>
ON <alias_name1>.<select_expr1> = <alias_name2>.<select_expr2>;
Si select_expr2 fait référence à une colonne de clé de partition, MaxCompute exécute la sous-requête en tant que tâche distincte au lieu de la convertir en LEFT ANTI JOIN. L'élagage des partitions s'applique toujours : les partitions dont les clés ne figurent pas dans le résultat de la sous-requête ne sont pas lues.
Syntaxe 2 — corrélée :
SELECT <select_expr1> FROM <table_name1>
WHERE <select_expr2> NOT IN (
SELECT <select_expr2> FROM <table_name2>
WHERE <table_name2_colname> = <table_name1>.<colname>
);
MaxCompute V2.0 prend en charge les expressions qui font référence aux colonnes de la sous-requête et de la requête externe. Ces conditions corrélées deviennent partie intégrante de la condition ON dans l'opération ANTI JOIN. MaxCompute V1.0 ne prend pas en charge ces expressions.
Lorsque NOT IN SUBQUERY ne peut pas être convertie en condition de jointure — par exemple, lorsqu'elle apparaît en dehors d'une clauseWHEREou lorsque la conditionWHEREinclut un opérateurANDconjointement avec NOT IN — MaxCompute l'exécute en tant que tâche distincte. Les conditions corrélées ne sont pas prises en charge dans ce cas.
Syntax 3 — sous-requête multi-colonnes :
Utilisez plusieurs colonnes dans l'expression NOT IN pour faire correspondre les résultats d'une sous-requête qui renvoie plusieurs colonnes. Les formes suivantes sont prises en charge :
Une instruction
SELECTsimple renvoyant plusieurs colonnesDes fonctions d'agrégation dans la sous-requête
Des tuples constants
Paramètres
| Paramètre | Obligatoire | Description |
|---|---|---|
select_expr1 |
Oui | Colonnes à renvoyer depuis la requête externe |
table_name1, table_name2 |
Oui | Les noms des tables externe et interne |
select_expr2, select_expr3 |
Oui | Colonnes à comparer ; les valeurs sont mises en correspondance positionnellement |
col_name |
Oui (Syntaxe 2) | Colonne utilisée comme condition corrélée |
Remarque d'utilisation
Les valeurs NULL dans le résultat de la sous-requête sont automatiquement exclues de la comparaison.
Exemples
Exemple 1 — Syntaxe 1 :
-- Create a shop1 table with an extra row not in sale_detail.
CREATE TABLE shop1 AS SELECT shop_name, customer_id, total_price FROM sale_detail;
INSERT INTO shop1 VALUES ('s8','c1',100.1);
SELECT * FROM shop1 WHERE shop_name NOT IN (SELECT shop_name FROM sale_detail);
Résultat :
+------------+-------------+-------------+
| shop_name | customer_id | total_price |
+------------+-------------+-------------+
| s8 | c1 | 100.1 |
+------------+-------------+-------------+
Exemple 2 — Syntaxe 2, condition corrélée :
SET odps.sql.allow.fullscan=true;
SELECT * FROM shop1
WHERE shop_name NOT IN (
SELECT shop_name FROM sale_detail
WHERE customer_id = shop1.customer_id
);
Résultat :
+------------+-------------+-------------+
| shop_name | customer_id | total_price |
+------------+-------------+-------------+
| s8 | c1 | 100.1 |
+------------+-------------+-------------+
Exemple 3 — NOT IN SUBQUERY qui ne peut pas être convertie en ANTI JOIN :
Lorsque la clause WHERE combine NOT IN avec une condition AND, MaxCompute ne peut pas convertir la sous-requête en LEFT ANTI JOIN et l'exécute plutôt en tant que tâche distincte.
SET odps.sql.allow.fullscan=true;
SELECT * FROM shop1
WHERE shop_name NOT IN (SELECT shop_name FROM sale_detail)
AND total_price < 100.3;
Résultat :
+------------+-------------+-------------+
| shop_name | customer_id | total_price |
+------------+-------------+-------------+
| s8 | c1 | 100.1 |
+------------+-------------+-------------+
Exemple 4 — Les valeurs NULL dans la table externe empêchent le renvoi de lignes :
-- Create a sale table that includes a row with NULL in shop_name.
CREATE TABLE IF NOT EXISTS sale
(
shop_name STRING,
customer_id STRING,
total_price DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);
ALTER TABLE sale ADD PARTITION (sale_date='2013', region='china');
INSERT INTO sale PARTITION (sale_date='2013', region='china')
VALUES ('null','null',null),('s2','c2',100.2),('s3','c3',100.3),('s8','c8',100.8);
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale WHERE shop_name NOT IN (SELECT shop_name FROM sale_detail);
Résultat : Aucune ligne renvoyée car la valeur NULL dans shop_name fait que l'expression NOT IN prend la valeur NULL pour chaque ligne.
+------------+-------------+-------------+------------+------------+
| shop_name | customer_id | total_price | sale_date | region |
+------------+-------------+-------------+------------+------------+
+------------+-------------+-------------+------------+------------+
Exemple 5 — Syntaxe 3, sous-requête multi-colonnes :
-- Use the same t1, t2 tables from the IN SUBQUERY examples.
-- Scenario 1: Simple SELECT with multiple columns.
SELECT a, b FROM t1 WHERE (c, d) NOT IN (SELECT a, b FROM t2 WHERE e = t1.e);
-- Result:
-- +------------+------------+
-- | a | b |
-- +------------+------------+
-- | 2 | 1 |
-- | 1 | 1 |
-- +------------+------------+
-- Scenario 2: Aggregate functions.
SELECT a, b FROM t1 WHERE (c, d) NOT IN (SELECT MAX(a), b FROM t2 WHERE e = t1.e GROUP BY b HAVING MAX(a) > 0);
-- Result:
-- +------------+------------+
-- | a | b |
-- +------------+------------+
-- | 1 | 3 |
-- | 3 | 1 |
-- | 2 | 1 |
-- | 1 | 1 |
-- +------------+------------+
-- Scenario 3: Constant tuples.
SELECT a, b FROM t1 WHERE (c, d) NOT IN ((1, 3), (1, 1));
-- Result:
-- +------------+------------+
-- | a | b |
-- +------------+------------+
-- | 1 | 3 |
-- | 2 | 1 |
-- | 1 | 1 |
-- +------------+------------+
EXISTS SUBQUERY
La clause EXISTS SUBQUERY renvoie les lignes de la requête externe pour lesquelles la sous-requête renvoie au moins une ligne. Si la sous-requête ne renvoie aucune ligne, la ligne externe est exclue.
MaxCompute prend en charge EXISTS SUBQUERY uniquement dans les clauses WHERE avec des conditions corrélées. La sous-requête est convertie en LEFT SEMI JOIN au moment de l'exécution.
Syntaxe
SELECT <select_expr> FROM <table_name1>
WHERE EXISTS (
SELECT <select_expr> FROM <table_name2>
WHERE <table_name2_colname> = <table_name1>.<colname>
);
Paramètres
| Paramètre | Obligatoire | Description |
|---|---|---|
select_expr |
Oui | Colonnes à renvoyer |
table_name1, table_name2 |
Oui | Les noms des tables externe et interne |
col_name |
Oui | Colonne utilisée comme condition corrélée |
Remarque d'utilisation
Les valeurs NULL dans le résultat de la sous-requête sont automatiquement exclues.
Exemple
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail
WHERE EXISTS (
SELECT * FROM shop WHERE customer_id = sale_detail.customer_id
);
-- Equivalent LEFT SEMI JOIN:
SELECT * FROM sale_detail a LEFT SEMI JOIN shop b ON a.customer_id = b.customer_id;
Résultat :
+------------+-------------+-------------+------------+------------+
| shop_name | customer_id | total_price | sale_date | region |
+------------+-------------+-------------+------------+------------+
| null | c5 | NULL | 2014 | shanghai |
| s6 | c6 | 100.4 | 2014 | shanghai |
| s7 | c7 | 100.5 | 2014 | shanghai |
| s1 | c1 | 100.1 | 2013 | china |
| s2 | c2 | 100.2 | 2013 | china |
| s3 | c3 | 100.3 | 2013 | china |
+------------+-------------+-------------+------------+------------+
NOT EXISTS SUBQUERY
La clause NOT EXISTS SUBQUERY est l'inverse de EXISTS SUBQUERY. Elle renvoie les lignes de la requête externe pour lesquelles la sous-requête ne renvoie aucune ligne.
MaxCompute prend en charge NOT EXISTS SUBQUERY uniquement dans les clauses WHERE avec des conditions corrélées. La sous-requête est convertie en LEFT ANTI JOIN au moment de l'exécution.
Syntaxe
SELECT <select_expr> FROM <table_name1>
WHERE NOT EXISTS (
SELECT <select_expr> FROM <table_name2>
WHERE <table_name2_colname> = <table_name1>.<colname>
);
Paramètres
| Paramètre | Obligatoire | Description |
|---|---|---|
select_expr |
Oui | Colonnes à renvoyer |
table_name1, table_name2 |
Oui | Les noms des tables externe et interne |
col_name |
Oui | Colonne utilisée comme condition corrélée |
Remarque d'utilisation
Les valeurs NULL dans le résultat de la sous-requête sont automatiquement exclues.
Exemple
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail
WHERE NOT EXISTS (
SELECT * FROM shop WHERE shop_name = sale_detail.shop_name
);
-- Equivalent LEFT ANTI JOIN:
SELECT * FROM sale_detail a LEFT ANTI JOIN shop b ON a.shop_name = b.shop_name;
Résultat : Aucune ligne, car chaque ligne de sale_detail possède une valeur shop_name correspondante dans shop.
+------------+-------------+-------------+------------+------------+
| shop_name | customer_id | total_price | sale_date | region |
+------------+-------------+-------------+------------+------------+
+------------+-------------+-------------+------------+------------+
SCALAR SUBQUERY
Une sous-requête scalaire renvoie exactement une ligne et une colonne. Le résultat peut être utilisé comme expression de valeur dans une comparaison de clause WHERE ou dans la liste SELECT. MaxCompute convertit les sous-requêtes scalaires en opérations JOIN lorsque cela est possible. Si une sous-requête scalaire qui renvoie une ligne comporte un seul opérateur MAX ou MIN imbriqué à l'extérieur, le résultat ne change pas.
Le type de retour doit être déterminable au moment de la compilation. Si MaxCompute ne peut confirmer que la sous-requête renvoie une seule ligne qu'au moment de l'exécution, le compilateur signale une erreur. Les modèles suivants satisfont à l'exigence de compilation :
La liste
SELECTde la sous-requête scalaire utilise des fonctions d'agrégation qui ne sont pas imbriquées dans une fonction table définie par l'utilisateur (UDTF)La sous-requête scalaire utilise des fonctions d'agrégation sans clause
GROUP BY
SCALAR SUBQUERY prend également en charge les résultats multi-colonnes, avec les règles suivantes :
Une sous-requête scalaire multi-colonnes dans la liste
SELECTdoit utiliser uniquement la comparaison d'égalitéLes colonnes d'une sous-requête scalaire multi-colonnes peuvent être de type booléen, mais seule la comparaison d'égalité est prise en charge
Une clause
WHEREpeut comparer les résultats d'une sous-requête scalaire multi-colonnes, mais seule la comparaison d'égalité est prise en charge
Syntaxe
SELECT <select_expr> FROM <table_name1>
WHERE (SELECT COUNT(*) FROM <table_name2>
WHERE <table_name2_colname> = <table_name1>.<colname>) <scalar_operator> <scalar_value>;
-- Equivalent JOIN form:
SELECT <table_name1>.<select_expr>
FROM <table_name1>
LEFT SEMI JOIN (
SELECT <colname>, COUNT(*) FROM <table_name2>
GROUP BY <colname>
HAVING COUNT(*) <scalar_operator> <scalar_value>
) <table_name2> ON <table_name1>.<colname> = <table_name2>.<colname>;
L'expression SELECT COUNT(*) FROM <table_name2> WHERE <table_name2_colname> = <table_name1>.<colname> renvoie un ensemble de lignes, mais comme la requête utilise un agrégat scalaire, le résultat est toujours une valeur unique. Sous cette forme, MaxCompute convertit la sous-requête scalaire en une jointure.
Paramètres
| Paramètre | Obligatoire | Description |
|---|---|---|
select_expr |
Oui | Colonnes à renvoyer |
table_name1, table_name2 |
Oui | Les noms des tables externe et interne |
col_name |
Oui | Colonne utilisée comme condition corrélée |
scalar_operator |
Oui | Un opérateur de comparaison : >, <, =, >= ou <= |
scalar_value |
Oui | La valeur scalaire à laquelle comparer |
Limitations
SCALAR SUBQUERY ne peut être utilisée que dans une clause WHERE, et non comme expression autonome dans la liste de sélection lorsqu'elle fait référence à des colonnes externes.
L'imbrication à plusieurs niveaux est autorisée, mais seule la référence de colonne la plus externe est accessible :
-- Valid: references t1 from the outermost subquery level.
SELECT * FROM t1 WHERE (SELECT COUNT(*) FROM t2 WHERE t1.a = t2.a) = 3;
-- Invalid: t1 is referenced from within a nested subquery — two levels deep.
SELECT * FROM t1 WHERE (SELECT COUNT(*) FROM t2 WHERE (SELECT COUNT(*) FROM t3 WHERE t3.a = t1.a) = 2) = 3;
Les modèles suivants sont également invalides — la liste SELECT d'une sous-requête scalaire ne peut pas faire référence à des colonnes externes :
-- Invalid: references outer column t1.b in the SELECT list.
SELECT * FROM t1 WHERE (SELECT t1.b + COUNT(*) FROM t2) = 3;
-- Invalid: SELECT list references outer column.
SELECT (SELECT COUNT(t1.a) FROM t2 WHERE t2.a = t1.a) FROM t1;
SELECT (SELECT t1.a FROM t2 WHERE t2.a = t1.a) FROM t1;
Exemples
Exemple 1 — comparaison de comptage agrégé :
SET odps.sql.allow.fullscan=true;
SELECT * FROM shop
WHERE (SELECT COUNT(*) FROM sale_detail WHERE sale_detail.shop_name = shop.shop_name) >= 1;
Résultat :
+------------+-------------+-------------+
| shop_name | customer_id | total_price |
+------------+-------------+-------------+
| s1 | c1 | 100.1 |
| s2 | c2 | 100.2 |
| s3 | c3 | 100.3 |
| null | c5 | NULL |
| s6 | c6 | 100.4 |
| s7 | c7 | 100.5 |
+------------+-------------+-------------+
Exemple 2 — sous-requête scalaire multi-colonnes :
-- Set up sample tables.
CREATE TABLE IF NOT EXISTS ts(a BIGINT, b BIGINT, c DOUBLE);
CREATE TABLE IF NOT EXISTS t(a BIGINT, b BIGINT, c DOUBLE);
INSERT INTO TABLE ts VALUES (1,3,4.0),(1,3,3.0);
INSERT INTO TABLE t VALUES (1,3,4.0),(1,3,5.0);
-- Scenario 1: Multi-column scalar subquery in the SELECT list (equality only).
-- Invalid: SELECT (SELECT a, b FROM t WHERE c > ts.c) AS (a, b), a FROM ts;
SELECT (SELECT a, b FROM t WHERE c = ts.c) AS (a, b), a FROM ts;
-- Result:
-- +------------+------------+------------+
-- | a | b | a2 |
-- +------------+------------+------------+
-- | 1 | 3 | 1 |
-- | NULL | NULL | 1 |
-- +------------+------------+------------+
-- Scenario 2: Boolean expression comparison (equality only).
-- Invalid: SELECT (a,b) > (SELECT a,b FROM ts WHERE c = t.c) FROM t;
SELECT (a,b) = (SELECT a,b FROM ts WHERE c = t.c) FROM t;
-- Result:
-- +------+
-- | _c0 |
-- +------+
-- | true |
-- | false|
-- +------+
-- Scenario 3: Multi-column WHERE comparison (equality only).
-- Invalid: SELECT * FROM t WHERE (a,b) > (SELECT a,b FROM ts WHERE c = t.c);
SELECT * FROM t WHERE c > 3.0 AND (a,b) = (SELECT a,b FROM ts WHERE c = t.c);
-- Result:
-- +------------+------------+------------+
-- | a | b | c |
-- +------------+------------+------------+
-- | 1 | 3 | 4.0 |
-- +------------+------------+------------+
SELECT * FROM t WHERE c > 3.0 OR (a,b) = (SELECT a,b FROM ts WHERE c = t.c);
-- Result:
-- +------------+------------+------------+
-- | a | b | c |
-- +------------+------------+------------+
-- | 1 | 3 | 4.0 |
-- | 1 | 3 | 5.0 |
-- +------------+------------+------------+
Limitations
Les limitations suivantes s'appliquent à tous les types de sous-requêtes :
| Type de sous-requête | Limitation |
|---|---|
| EXISTS SUBQUERY | Pris en charge uniquement dans les clauses WHERE avec des conditions corrélées |
| NOT EXISTS SUBQUERY | Pris en charge uniquement dans les clauses WHERE avec des conditions corrélées |
| IN SUBQUERY (non-JOIN) | Conditions corrélées non prises en charge lorsque la sous-requête s'exécute en tant que tâche distincte |
| NOT IN SUBQUERY (non-JOIN) | Conditions corrélées non prises en charge lorsque la sous-requête s'exécute en tant que tâche distincte |
| SCALAR SUBQUERY | Utilisable uniquement dans les clauses WHERE ; la liste SELECT de la sous-requête ne peut pas faire référence à des colonnes externes |
| SCALAR SUBQUERY (multi-niveaux) | Seule la référence de colonne externe la plus haute est accessible |
| SCALAR SUBQUERY (multi-colonnes) | Seule la comparaison d'égalité est prise en charge |
| MaxCompute V1.0 | IN SUBQUERY et NOT IN SUBQUERY ne prennent pas en charge les expressions qui font référence aux colonnes de la sous-requête et de la requête externe |
Étapes suivantes
Un grand nombre de sous-requêtes ou des modèles de sous-requêtes inefficaces peuvent ralentir les requêtes dans les environnements Big Data. Envisagez ces alternatives :
Remplacez les sous-requêtes fréquemment réutilisées par des tables temporaires ou des vues matérialisées pour éviter les recalculs. Consultez la section Recommandations et gestion des vues matérialisées.
Réécrivez plusieurs sous-requêtes sous la forme d'une seule jointure JOIN pour réduire la charge des tâches. Consultez la section JOIN.