Tâches et solutions pour PostgreSQL Fighter


Salutations à tous les amateurs de SQL!

Sur Internet, j'ai rarement vu des articles couvrant différents points de travail et subtilités associés au traitement des données en SQL .
J'aime quand vous pouvez apprendre beaucoup de choses d'un article à la fois, même en termes généraux.
Par conséquent, j'ai décidé d'écrire mon article contenant diverses tâches et réponses avec des explications à leur sujet.
Convient à ceux qui maîtrisent bien toutes les compétences de base et souhaitent se développer davantage.

Les réponses fournies conviennent à PostgreSQL (la plupart des tâches conviendront à d'autres SGBD , mais les résultats et les solutions peuvent être différents. C'est même intéressant où les différences surviennent)

Essayez de vous répondre avant d'ouvrir le spoiler.

C'est parti!


Je vais essayer de marquer avec un astérisque quelque chose de purement pour PostgreSQL * (il n'y a pas beaucoup de tels moments)

1. Un peu sur les opérations numériques


1.1 Ces demandes seront-elles satisfaites? Quels résultats reviendront-ils?

-- )     SELECT 3/2; -- ) SELECT min('- '::TEXT), avg('- '::TEXT); -- )*      FALSE,     ? SELECT 7.2 = (3.8::FLOAT + 3.4) -- ) SELECT (20/25)*25.0; 


Réponses à 1.1
A) Réponse: 1
Seule la partie entière sera affichée, car l'opération utilise des entiers. Cela se trouve souvent dans d'autres langues.

B) Réponse: la demande ne sera pas exécutée .

avg donnera une erreur puisque accepte uniquement les nombres et les intervalles de temps *

Cependant, la fonction min / max peut être exécutée sur des données de texte (conformément au tri alphabétique dans la base de données).
Parfois, cela peut être utile lorsque vous devez au moins consulter une colonne qui n'est pas répertoriée dans GROUP BY
Ou lorsque vous devez appliquer un tri alphabétique aux nombres, dans lequel '10' <'2'

B) Réponse: FAUX

Cela peut sembler étrange, mais cela est acceptable , car il s'agit d'une caractéristique d'un ordinateur représentant certains nombres à virgule flottante; un nombre peut prendre la forme 7.1 (9)
Je me rappelle comment j'ai traité une demande pendant longtemps sans le savoir.

D) Réponse: 0 . le hic est que l'expression entre parenthèses sera = 0

SELECT (20 / 25.0) * 25 fonctionnerait plus correctement


1.2 Étant donné la table " table_2 " (avec une seule colonne " valeur " (INTEGER)) composée des 5 lignes suivantes:
valeur
5
5
Null
5
5

Quel résultat renverra la requête:
 SELECT (avg(value)*count(*)) - sum(value) FROM table_2; 

Options de réponse
  • -4
  • 0
  • Null
  • 5
  • Cela provoquera une erreur car non spécifié GROUP BY
  • Aucun des inscrits


Réponse 1.2
réponse: 5

Les fonctions d'agrégation appliquées à une colonne particulière ignorent NULL , mais count (*) comptera toutes les lignes
5 * 5 - 20


2. Problèmes généraux


2.1 Dans quels cas une requête ne peut-elle pas renvoyer tout le contenu d'une table? ( parent_id INTEGER, le tableau est rempli d'une variété de données)

  SELECT * FROM any_table WHERE parent_id = parent_id; 

Comment se comportera la demande ci-dessous? Quelles données sortiront-elles? * PostgreSQL

  SELECT * FROM any_table WHERE parent_id IS NOT DISTINCT FROM parent_id; 

Réponses à 2.1
La première requête affichera toutes les entrées sauf celles où parent_id est NULL

La deuxième requête affichera toutes les entrées de table. IS DISTINCT FROM est logiquement similaire à l'opérateur ! = Dans lequel NULL est identique à NULL
N'EST PAS DISTINCT DE transforme logiquement l'inégalité en égalité

2.2. Quel sera le résultat de la demande?

 -- ) SELECT * FROM ( SELECT 1 UNION ALL SELECT 1 ) x(y) UNION ( SELECT 2 UNION ALL SELECT 2 ); 

Répondre à 2.2
Le résultat sera 2 lignes avec les valeurs 1 et 2 , UNION supprimera tous les doublons dans la sélection résultante , et pas seulement entre les deux tables jointes. J'ai remarqué que ce n'est pas évident pour tout le monde.

2.3 Écrivez une requête qui affiche la date de demain.

Réponse à 2.3
 SELECT CAST((now()+ INTERVAL '1 DAY') AS DATE) 

Tout le monde ne travaille pas souvent avec des dates, mais cela vaut la peine de maîtriser un minimum
* Solution Postgres, mais je pense que les autres SGBD ne sont pas très différents

Si travailler avec des dates est nouveau pour vous, je vous conseille d'expérimenter la demande
Par exemple:
- remplacez DAY par (semaine, mois, année, etc.)
- remplacer +1 par -9000
- remplacer DATE par TIME
- supprimer CAST
- laissez seulement MAINTENANT ()
etc.

Et, inspiré par certains résultats, allez lire le MANUEL , tous les sujets y sont décrits en détail


2.4 Les instructions UPDATE , DELETE , INSERT et MERGE sont conçues pour manipuler les données des tableaux. SELECT .. l' exécution est-elle "sûre"? Une requête peut-elle affecter les données de la table?
Réponse à 2.4
La question peut cependant sembler primitive ...

Au tout début de l'apprentissage de SQL, j'avais l'avis que cette déclaration ne peut afficher que des données, mais:

Outre le fait que SELECT est capable de verrouiller la table pour le changement (BEGIN; SELECT ... FOR UPDATE) *
SELECT est capable d'appeler des fonctions qui peuvent effectuer presque toutes les manipulations.

Les débutants doivent comprendre cela tout de suite, et non après avoir rempli la "petite demande d'information" sur le serveur de production


3. Uniquement PostgreSQL


3.1 Décrivez ce qui se passe lorsque cette requête est exécutée dans la boîte de dialogue SQL:

 SELECT * INTO wtf FROM pg_stat_activity; 

Répondre à 3.1
En règle générale, SELECT INTO est utilisé dans les fonctions plpgsql pour écrire une valeur dans une variable.

En dehors de plpgsql, l'effet de la commande sera similaire à la requête ci-dessous:

 CREATE TABLE wtf AS SELECT * FROM pg_stat_activity; 


3.2 Que montrera cette "simple" demande

 SELECT wtf_ FROM pg_stat_activity AS wtf_ ; 

Répondre à 3.2
pg_stat_activity vue système (VIEW) des processus actifs dans la base de données.

La particularité de la requête est qu'une colonne avec des lignes (ROW) ayant TYPE pg_stat_activity (ou une autre table) sera affichée. Vous devez le savoir plus tôt pour ceux qui écrivent des fonctions. Vous pouvez en lire plus dans le manuel.
La question a été ajoutée car un débutant peut facilement obtenir un tel résultat par erreur et ne pas comprendre quel est le problème

4. Travaillez avec du texte. Expressions régulières


Je pense que vous devez être en mesure non seulement de générer des requêtes, mais également de présenter les résultats de la bonne manière.
Les expressions régulières sont un énorme sujet distinct, avec de nombreux articles de qualité. Par conséquent, je ne montrerai que des exemples, sans explications détaillées.

4.1. Supposons qu'il existe une table " table_5 " avec une colonne de texte " X " et de nombreuses lignes différentes. Quelle requête peut obtenir les 10 derniers caractères de chaque ligne?

Répondre à 4.1
SQL vous permet de trouver de nombreuses solutions au même problème, par exemple:
la chose la plus simple qui me vient à l'esprit est la bonne (X, 10)
regex peut être utilisé: sous - chaîne (X, '. {0,10} $')
vous pouvez même nakostylyat "esquiver" (dans tous les sens) comme ceci: reverse (substring (reverse (X) for 10))


4.2 Il existe une table «table_6» avec une colonne de texte «X». Le tableau contient une ligne (tout le texte en anglais et en russe uniquement):
 'Lorem 3 Ipsum 23 standard 7 dummy 17 text Ultimate Answer of Life ?? 777' 

A) Écrivez une requête qui renverra les 42e à 68e caractères de cette chaîne
B) Comment extraire uniquement des lettres MAJUSCULES (russe ou anglais) dans une chaîne en utilisant SQL?
C) Comment calculer la somme des nombres ( pas des chiffres ) dans une chaîne en utilisant SQL

Esquisse SQL
 WITH table_6(X) AS( SELECT 'Lorem 3 Ipsum 23 standard 7 dummy 17 text Ultimate Answer of Life ?? 777'::TEXT ) SELECT X FROM table_6 

Réponses à 4.2
  --    WITH  "SQL " -- ) SELECT SUBSTRING(LEFT(X,68) FROM 42 ) FROM table_6 -- 1  SELECT SUBSTRING(X, 42, (68-42)+1) FROM table_6 -- 2  -- 3    -- )  ,        SELECT regexp_replace(X,'[^A-Z-]', '','g') FROM table_6 --  ''      - --   'g'    1  -- )        --   regexp_matches   **     ,      SELECT sum(x[1]::INT) FROM ( SELECT regexp_matches(X,'[0-9]+','g') FROM table_6 ) AS y(x) -- *        -- **   +,     (   1 ,     ) 


4.3 Comment remplacer tous les espaces doubles (triples ou plus) par un seul espace dans le texte (cellule du tableau)? (par tradition: la table " table_7 " avec la colonne " X ") (PS il suffira d'écrire SELECT renvoyant le résultat souhaité, et non UPDATE table_7 ... )

Répondre à 4.3
 WITH table_7(X) AS (SELECT 'Lorem 3 Ipsum 23 standard 7 dummy 11 text'::TEXT) -- 1 .    (2   ) SELECT regexp_replace(X, '( ){2,}', ' ', 'g') FROM table_7 -- 2 .     (,  ,    ..)   ,      SELECT regexp_replace(X, '\s+', ' ', 'g') FROM table_7 --  !  ,  -    ,    "" .    .. --   ,    , ,        --    ,      ,    SELECT replace(replace(replace(X, ' ', '<>'), '><', ''), '<>', ' ') FROM table_7 


4.4 Il existe une chaîne " X " dans laquelle les fautes de frappe sont autorisées. Au lieu des lettres russes (e, o, s, C), des caractères similaires de l'extérieur de l'alphabet anglais ont été utilisés. Remplacez ces caractères par SQL.

PS La ligne ne doit contenir que des caractères russes, et vous ne devez pas vous inquiéter d'un éventuel changement de mots anglais.

(Si vous éprouvez des difficultés à remplacer tous les personnages, remplacez-en au moins un)

Exemple de ligne:

X = 'Coeo eoc oe'

Répondre à 4.4
 -- , Replace(Replace(Replace(..  ,  --        (1   1 ) SELECT TRANSLATE('Coeo  eoc oe', 'Cceo', '') 

4.5 Écrivez une requête qui convertit une chaîne:
« Ivan Ivanov et Ivanovich» à l'espèce «Ivan Ivanov

Répondre à 4.5
 --  ,     SELECT initcap('  ') *      

Quête bonus pour ceux qui ont réussi
Idéal s'il y a une fonction prête à l'emploi
Pouvez-vous convertir l'inverse? (de préférence sans perdre de rembourrage).
Peut-être que la tâche n'est pas typique, mais elle sera utile pour le développement.

«IVANOV IVAN IVANOVICH» converti en «IVANOV IVAN IVANOVICH»
et inverser le cas?

Réponse au défi bonus
 SELECT string_agg(LOWER(LEFT(x,1)) || UPPER(SUBSTRING(x from 2)), '' ORDER BY rn) FROM (SELECT * FROM regexp_split_to_table('    4 TesT', '\y') WITH ORDINALITY y(x, rn) ) AS z -- *  PostgreSQL,      --    ,     --      ,      --      . -- WITH ORDINALITY      (   9.4) --      --          -- .. 

5. Un peu sur les transactions


Les transactions sont une chose très importante dans un SGBD, il est important de comprendre les principaux points.

Je vais essayer de simuler un exemple:

Supposons qu'il existe une table «biens» avec laquelle deux utilisateurs vont travailler.
Il a une colonne de remise entière égale à 10 pour toutes les lignes.
Les paramètres de la base de données sont standard (READ COMMITTED - lecture des données validées).

L'utilisateur User_1 ouvre une transaction, exécute la demande suivante:

 BEGIN; UPDATE goods SET discount = discount + 5; 

Une seconde plus tard, un autre utilisateur ( User_2 )
Il effectue presque la même demande sans ouvrir de transaction:
 UPDATE goods SET discount = discount + 10; 

Que pensez-vous qu'il se passera dans les situations suivantes:

A) Quel résultat l'Utilisateur_2 obtiendra-t-il si l'Utilisateur_1 laisse la transaction ouverte (c'est-à-dire qu'il ne confirme pas la transaction / n'annule pas les modifications)?
Ce que User_1 verra sur demande:

 SELECT discount FROM goods LIMIT 1; 

B) Que se passe-t-il si User_1 fait ROLLBACK? Quels résultats obtiendra User_2?

Q) Que se passe-t-il si User_1 COMMIT? Quels résultats obtiendra User_2?

Les réponses
Pour autant que je sache, READ UN COMMITTED n'est pas pris en charge dans PostgreSQL, et les données sales (non confirmées) ne peuvent pas être lues

Les réponses seront les suivantes:

A) La requête User_2 attendra COMMIT ou ROLLBACK de User_1. (la demande semble geler)
L'utilisateur_1 dans sa transaction verra sa version de l'instantané de la base de données, où la remise est déjà égale à 15

B) Si User_1 effectue un ROLLBACK, la valeur de la remise restera la même, puis User_2 sera exécuté, ce qui ajoutera 10 à la remise et la remise sera de 20

C) Si User_1 effectue COMMIT, la valeur de la remise augmentera de 5, puis User_2 sera exécutée, ce qui ajoutera 10 à la remise et la remise sera de 25

Une autre version de cette tâche
Une version légèrement différente de la tâche 13 de l'utilisateur kirill_petrov sur READ COMMITTED
 --      CREATE TABLE goods (discount) AS (SELECT 10::INT UNION ALL SELECT 15); -- 1. User_1   (  ): BEGIN; UPDATE goods SET discount = discount + 5; --2. User_2  : UPDATE goods SET discount = discount + 100 WHERE discount = 15 --3. User_1  COMMIT; 
Quelles données figureront dans le tableau?

Conclusion


Je pense que cela a touché des points assez intéressants.

J'espère que les tâches aideront à motiver les débutants, car il est ennuyeux d'apprendre quelque chose sans buts / objectifs / directions spécifiques.

Je peux être heureux pour ceux qui ont été faciles à répondre à toutes les questions. Et ceux qui ont eu des difficultés, je l'espère, se sont lancés dans la voie du développement. Ceux qui ne comprennent pas grand-chose mais veulent apprendre le SQL, j'invite le cours PostgreSQL pour les jeunes combattants à mon dernier article.

J'attends avec impatience tous les ajouts, solutions à des problèmes particulièrement intéressants (vous pouvez exploiter) et d'autres commentaires!

Merci de votre attention! Je vous souhaite beaucoup de succès dans l'apprentissage du SQL!

Source: https://habr.com/ru/post/fr423097/


All Articles