mise à jour des lignes de table dans postgres à l'aide d'une sous-requête

304

En utilisant postgres 8.4, mon objectif est de mettre à jour la table existante:

CREATE TABLE public.dummy
(
  address_id SERIAL,
  addr1 character(40),
  addr2 character(40),
  city character(25),
  state character(2),
  zip character(5),
  customer boolean,
  supplier boolean,
  partner boolean

)
WITH (
  OIDS=FALSE
);

Au départ, j'ai testé ma requête en utilisant l'instruction insert:

insert into address customer,supplier,partner
SELECT  
    case when cust.addr1 is not null then TRUE else FALSE end customer, 
    case when suppl.addr1 is not null then TRUE else FALSE end supplier,
    case when partn.addr1 is not null then TRUE else FALSE end partner
from (
    SELECT *
        from address) pa
    left outer join cust_original cust
        on (pa.addr1=cust.addr1 and pa.addr2=cust.addr2 and pa.city=cust.city 
            and pa.state=cust.state and substring(cust.zip,1,5) = pa.zip  )
    left outer join supp_original suppl 
        on (pa.addr1=suppl.addr1 and pa.addr2=suppl.addr2 and pa.city=suppl.city 
                and pa.state=suppl.state and pa.zip = substring(suppl.zip,1,5))
    left outer join partner_original partn
        on (pa.addr1=partn.addr1 and pa.addr2=partn.addr2 and pa.city=partn.city
                  and pa.state=partn.state and pa.zip = substring(partn.zip,1,5) )
where pa.address_id = address_id

étant novice, je ne parviens pas à convertir pour mettre à jour l'instruction, c'est-à-dire mettre à jour les lignes existantes avec les valeurs renvoyées par l'instruction select. Toute aide est grandement appréciée.

empilement
la source
avez-vous un type d'ID dans la table d'adresses, qui peut être utilisé pour déterminer que cette ligne existe?
Andrey Adamovich
oui je le fais mais son système est généré.
stackover

Réponses:

683

Postgres permet:

UPDATE dummy
SET customer=subquery.customer,
    address=subquery.address,
    partn=subquery.partn
FROM (SELECT address_id, customer, address, partn
      FROM  /* big hairy SQL */ ...) AS subquery
WHERE dummy.address_id=subquery.address_id;

Cette syntaxe n'est pas du SQL standard, mais elle est beaucoup plus pratique pour ce type de requête que le SQL standard. Je crois qu'Oracle (au moins) accepte quelque chose de similaire.

Andrew Lazarus
la source
il semble que j'essaye quelque chose d'un peu différent, par exemple. s'il y a 3 bool colonnes c1, c2, c3 toutes initialisées à false au départ. mais en fonction de la sous-requête sont définies sur true. mise à jour set c1 = TRUE where id in (subquery1), set c2 = TRUE where id in (subquery2), set c3 = True where id in (subquery3). J'ai réussi quand je divise cela en 3 mises à jour mais je ne sais pas comment atteindre le résultat avec une seule mise à jour. espérons que cela a du sens.
stackover
3
FWIW, Oracle accepte cette construction de base, mais les performances de la mise à jour ont tendance à se dégrader sérieusement à mesure que les tables grossissent. Ce n'est pas grave car Oracle prend également en charge l'instruction MERGE.
gsiems
3
Cela ne fonctionne pas du tout dans postgresql 9.5, je reçoisERROR: 42P01: relation "dummy" does not exist
user9645
73
dummydoit être remplacé par le nom de la table que vous essayez de mettre à jour. Veuillez comprendre la question et la réponse avant d'essayer de postuler.
Andrew Lazarus
1
Il peut être utile de mentionner qu'au début de la requête, il n'est pas nécessaire de spécifier le chemin d'accès à la colonne du côté gauche, uniquement à la fin, sinon la base de données se plaindra avec ERREUR: la référence de colonne "adresse_id" est ambiguë
OJVM
51

S'il n'y a aucun gain de performances en utilisant une jointure, je préfère les expressions de table communes (CTE) pour la lisibilité:

WITH subquery AS (
    SELECT address_id, customer, address, partn
    FROM  /* big hairy SQL */ ...
)
UPDATE dummy
SET customer = subquery.customer,
    address  = subquery.address,
    partn    = subquery.partn
FROM subquery
WHERE dummy.address_id = subquery.address_id;

À mon humble avis un peu plus moderne.

steevee
la source
1
La syntaxe n'est pas compatible avec les anciennes versions de Postgres, avant la v9.1, (voir postgresql.org/docs/9.1/static/sql-update.html et les versions précédentes) Je suis sur la v8.2, vous avez donc pour mettre l'intégralité de l'instruction CTE / With entre crochets après le mot clé FROM et cela fonctionnera.
Spcogg le deuxième
9

Il existe plusieurs façons de mettre à jour les lignes.

En ce qui concerne UPDATEles lignes utilisant des sous-requêtes, vous pouvez utiliser n'importe laquelle de ces approches.

  1. Approche-1 [Utilisation d'une référence de table directe]
UPDATE
  <table1>
SET
  customer=<table2>.customer,
  address=<table2>.address,
  partn=<table2>.partn
FROM
  <table2>
WHERE
  <table1>.address_id=<table2>.address_i;

Explication: table1est la table que nous voulons mettre à jour, table2 est la table, à partir de laquelle nous obtiendrons la valeur à remplacer / mettre à jour. Nous utilisons la FROMclause, pour récupérer les table2données de. WHERE La clause aidera à définir le mappage de données approprié.

  1. Approche-2 [Utilisation de sous-requêtes]
UPDATE
  <table1>
SET
  customer=subquery.customer,
  address=subquery.address,
  partn=subquery.partn
FROM
  (
    SELECT
      address_id, customer, address, partn
    FROM  /* big hairy SQL */ ...
  ) AS subquery
WHERE
  dummy.address_id=subquery.address_id;

Explication: Ici, nous utilisons une sous-requête à l'intérieur de la FROMclause et nous lui donnons un alias. Pour qu'il agisse comme la table.

  1. Approche-3 [Utilisation de plusieurs tables jointes]
UPDATE
  <table1>
SET
  customer=<table2>.customer,
  address=<table2>.address,
  partn=<table2>.partn
FROM
  <table2> as t2
  JOIN <table3> as t3
  ON
    t2.id = t3.id
WHERE
  <table1>.address_id=<table2>.address_i;

Explication: Parfois, nous sommes confrontés à la situation dans laquelle la jointure de table est si importante pour obtenir les données appropriées pour la mise à jour. Pour ce faire, Postgres nous permet de joindre plusieurs tables à l'intérieur de la FROMclause.

  1. Approche-4 [Utilisation de l'instruction WITH]

    • 4.1 [Utilisation d'une requête simple]
WITH subquery AS (
    SELECT
      address_id,
      customer,
      address,
      partn
    FROM
      <table1>;
)
UPDATE <table-X>
SET customer = subquery.customer,
    address  = subquery.address,
    partn    = subquery.partn
FROM subquery
WHERE <table-X>.address_id = subquery.address_id;
  • 4.2 [Utilisation d'une requête avec JOIN complexe]
WITH subquery AS (
    SELECT address_id, customer, address, partn
    FROM
      <table1> as t1
    JOIN
      <table2> as t2
    ON
      t1.id = t2.id;
    -- You can build as COMPLEX as this query as per your need.
)
UPDATE <table-X>
SET customer = subquery.customer,
    address  = subquery.address,
    partn    = subquery.partn
FROM subquery
WHERE <table-X>.address_id = subquery.address_id;

Explication: Depuis Postgres 9.1, ce WITHconcept ( ) a été introduit. En utilisant cela, nous pouvons faire des requêtes complexes et générer le résultat souhaité. Ici, nous utilisons cette approche pour mettre à jour le tableau.

J'espère que ce serait utile.😊

Mayur
la source
1
update json_source_tabcol as d
set isnullable = a.is_Nullable
from information_schema.columns as a 
where a.table_name =d.table_name 
and a.table_schema = d.table_schema 
and a.column_name = d.column_name;
Pugazendhi Asaimuthu
la source
1

@Mayur "4.2 [Utilisation d'une requête avec JOIN complexe]" avec des expressions de table communes (CTE) a fait l'affaire pour moi.

WITH cte AS (
SELECT e.id, e.postcode
FROM employees e
LEFT JOIN locations lc ON lc.postcode=cte.postcode
WHERE e.id=1
)
UPDATE employee_location SET lat=lc.lat, longitude=lc.longi
FROM cte
WHERE employee_location.id=cte.id;

J'espère que cela aide ...: D

Festus Ngor
la source