PostgreSQL'de VALUES kullanarak geçici bir tablo nasıl oluşturulur?


38

PostgreSQL'i öğreniyorum ve WITHhata ayıklama amacıyla geçici bir tablo veya normal tablo yerine kullanılabilecek bir bildirim nasıl oluşturulacağını bulmaya çalışıyorum .

CREATE TABLE dokümantasyonuna baktım ve VALUESsorgu olarak kullanılabileceğini söylüyor ; VALUESOraya bağlanan maddeye ilişkin belgelerin de bir örneği yok mu?

Bu yüzden aşağıdaki gibi basit bir test yazdım:

DROP TABLE IF EXISTS lookup;
CREATE TEMP TABLE lookup (
  key integer,
  val numeric
) AS
VALUES (0,-99999), (1,100);

Ancak PostgreSQL (9.3) hakkında şikayetçi

"AS" de veya yakınında sözdizimi hatası

Benim sorularım:

  1. Yukarıdaki ifadeyi nasıl düzeltebilirim?

  2. A'da kullanılmak üzere nasıl uyarlayabilirim WITH block?

Şimdiden teşekkürler.


Ben (onaylanmamış bir sigara standardize sözdizimi kullanıyor seçilen cevap olarak) biraz daha modern tavsiyesi ile bu soruyu cevaplamak için çalışmıştı dba.stackexchange.com/a/201575/2639
Evan Carroll

Yanıtlar:


46

EDIT: Orijinal kabul edilen cevabı olduğu gibi bırakıyorum, ancak lütfen a_horse_with_no_name tarafından önerilen aşağıdaki düzenlemenin DEĞERLER kullanarak geçici bir tablo oluşturmak için tercih edilen yöntem olduğunu unutmayın.

Eğer sadece bir tablo oluşturmak ve içine eklemek yerine, sadece bazı değerler arasından seçim yapmak istiyorsanız, şöyle bir şey yapabilirsiniz:

WITH  vals (k,v) AS (VALUES (0,-9999), (1, 100)) 
SELECT * FROM vals;

Aslında benzer şekilde geçici bir tablo oluşturmak için aşağıdakileri kullanın:

WITH  vals (k,v) AS (VALUES (0,-9999), (1, 100)) 
SELECT * INTO temporary table temp_table FROM vals;

DÜZENLEME: As, a_horse_with_no_name tarafından işaret docs o belirtmektedir CREATE TABLE AS...işlevsel olarak benzer olan SELECT INTO ..., ancak eski ikincisi bir üst olduğunu ve bu SELECT INTO- bu başarısız olur böylece geçici bir değişkene bir değer atamak için plpgslq kullanılır O vaka. Bu nedenle, yukarıdaki örnekler düz SQL için geçerli olsa da, CREATE TABLEform tercih edilmelidir.

CREATE TEMP TABLE temp_table AS                                     
WITH t (k, v) AS (
 VALUES
 (0::int,-99999::numeric), 
 (1::int,100::numeric)
)
SELECT * FROM t;

Ayrıca, a_horse_with_no_name tarafından yazılan yorumlardan ve OP'nin orijinal sorusundan, bu değerler listesindeki doğru veri tiplerine bir döküm içerdiğini ve bir CTE (WITH) ifadesi kullandığını not edin.

Ayrıca, Evan Carrol'un cevabında işaret edildiği gibi, bir CTE sorgusu bir optimizasyon çitidir , yani CTE her zaman gerçekleşmiştir. CTE'leri kullanmanın birçok iyi nedeni vardır, ancak dikkatli kullanılmazsa oldukça önemli bir performans artışı olabilir. Bununla birlikte, optimizasyon çitinin gerçekten performansı artırabildiği birçok örnek vardır, bu yüzden bu, farkında olmamak, körlemekten kaçınmamak için bir şeydir.


12
dokümanlardan : " CREATE TABLE AS, işlevsel olarak SELECT
INTO'ya

Optimizasyon çiti mutlaka kötü bir şey değildir. Bundan dolayı daha hızlı koşabilmek için ayarlayabileceğim birçok ifade gördüm.
a_horse_with_no_name

Tabii ki bunu da açıklığa kavuşturdum. CTE'leri her zaman mekansal bir bağlamda kullanırım. Gibi bir şey ile bir yerde maddesi varsa WHERE ST_Intersects(geom, (SELECT geom FROM sometable)veya WHERE ST_Intersects(geom, ST_Buffer(anothergeom, 10)daha sonra sık sık sorgu planlayıcısı mekansal indeksi kullanmaz çünkü artık sargable içinde geom sütunu. İlk CTE'de ilgi alanınızı yaratırsanız, bu sorun ortadan kalkar. Aynı sorguyla aynı aoi'yi birden fazla ifadede kullanmak istiyorsanız, bir CBS bağlamında nadir olmayan bir durumdur.
John Powell,

25

create table as select ifadesi gerekiyor:

DROP TABLE IF EXISTS lookup;
CREATE TEMP TABLE lookup 
as 
select *
from (
   VALUES 
    (0::int,-99999::numeric), 
    (1::int, 100::numeric)
) as t (key, value);

CTE kullanmak için bunu yeniden de yazabilirsiniz:

create temp table lookup 
as 
with t (key, value) as (
  values 
    (0::int,-99999::numeric), 
    (1::int,100::numeric)
)
select * from t;

1
Yorumunuz için teşekkürler. Siz dokümanlarda belirtilen sebeplerden dolayı yaklaşımınız açıkça daha iyidir. Neredeyse 5 yıl geç olsa da cevabımı değiştirdim.
John Powell,

11

Mesele veri türleri. Bunları kaldırırsanız, ifade çalışacaktır:

CREATE TEMP TABLE lookup
  (key, val) AS
VALUES 
  (0, -99999), 
  (1, 100) ;

Türleri ilk satırın değerlerini yayınlayarak tanımlayabilirsiniz:

CREATE TEMP TABLE lookup 
  (key, val) AS
VALUES 
  (0::bigint, -99999::int), 
  (1, 100) ;

3

İhtiyacınız olan tek şey sorgularınızda birkaç değer kullanmaksa, gerçekten bir tablo oluşturmanıza veya bir CTE kullanmanıza gerek yoktur. Onları satır içi yapabilirsiniz:

SELECT  *
FROM    (VALUES(0::INT, -99999::NUMERIC), (1, 100)) AS lookup(key, val)

Daha sonra, bir Kartezyen ürününü CROSS JOIN(diğer ilişkinin tabii ki normal bir tablo, görünüm vb. Olduğu yerlerde) elde edebilirsiniz. Örneğin:

SELECT  *
FROM    (VALUES(0::int, -99999::numeric), (1, 100)) AS lookup(key, val)
       ,(VALUES('Red'), ('White'), ('Blue')) AS colors(color);

hangi verim:

key |val    |color |
----|-------|------|
0   |-99999 |Red   |
1   |100    |Red   |
0   |-99999 |White |
1   |100    |White |
0   |-99999 |Blue  |
1   |100    |Blue  |

Veya JOINbaşka bir ilişkiye sahip olan değerler (yine normal bir tablo, görünüm vb. Olabilir), örneğin:

SELECT  *
FROM    (VALUES(0::int, -99999::numeric), (1, 100)) AS lookup(key, val)
  JOIN  (VALUES('Red', 1), ('White', 0), ('Blue', 1)) AS colors(color, lookup_key)
          ON colors.lookup_key = lookup.key;

hangi verim:

key |val    |color |lookup_key |
----|-------|------|-----------|
1   |100    |Red   |1          |
0   |-99999 |White |0          |
1   |100    |Blue  |1          |

Tamam ama soru "nasıl geçici bir tablo oluşturulur ...?"
ypercubeᵀᴹ

Evet, fakat başka bir ilişkiye katılmamak için neden birkaç sabit arama değerine sahip geçici bir tabloya ihtiyacınız olsun ki? Bu çözüm, sorunun nasıl ifade edildiğine bakılmaksızın sorunu kendisi çözer.
isapir

1
Belki OP sadece örneği soru olarak göndermesi kolay bir şeye indirgemiştir, ancak gerçek verilerin binlerce değeri vardır?
stannius,

OP, değerleri kullanarak özellikle belirtti , bu yüzden cevabım hala aynen böyle olduğu gibi hala geçerli
isapir

2

İlk önce her zaman standardize edilmiş kullanımı, diğer cevaplarda önerildiği gibi CREATE TABLE AS, SELECT INTOon yıldan fazla süredir kullanımdan kaldırılmış bir sözdizimi olmuştur. CTE ile kullanabilirsinizCREATE TABLE AS

Buradaki birçok cevap bir CTE kullanmanızı önerirken, bu tercih edilmez. Aslında, muhtemelen biraz daha yavaş. Sadece masa olarak sarın.

DROP TABLE IF EXISTS lookup;

CREATE TEMP TABLE lookup(key, value) AS
  VALUES
  (0::int,-99999::numeric),
  (1,100);

Eğer bir select ifadesi yazmak zorundaysanız, bunu da yapabilirsiniz (ve bir CTE'ye ihtiyacınız yoktur).

CREATE TEMP TABLE lookup(key, value) AS
  SELECT key::int, value::numeric
  FROM ( VALUES
    (0::int,-99999::numeric),
    (1,100)
  ) AS t(key, value);

PostgreSQL'deki bir CTE materyalleşmeyi zorlar. Bir optimizasyon çiti. Bu nedenle, maliyetleri anladığınız ve performans iyileştirmesi sağladığınızı bilmediğiniz sürece, bunları herhangi bir yerde kullanmak genellikle iyi bir fikir değildir. Yavaşlamayı burada görebilirsiniz, örneğin,

\timing
CREATE TABLE foo AS
  SELECT * FROM generate_series(1,1e7);
Time: 5699.070 ms

CREATE TABLE foo AS
  WITH t AS ( SELECT * FROM generate_series(1,1e7) ) 
  SELECT * FROM t;
Time: 6484.516 ms

Cevabı standardı yansıtacak şekilde güncelledim ve kabul edilen cevabın her zaman CREATE TABLE AS ile eşdeğer olmadığını ve optimizasyon çiti hakkında bir yorum eklediğimi belirttim. CTE'ler pek çok avantaj getiriyor, ancak doğru kullanılırsa, kör kullanılırsa korkunç performansa yol açabilir.
John Powell,

-2
WITH u AS (
    SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS account (id,name)
)
SELECT id, name, length(name) from u;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.