Periksa tercepat apakah ada baris di PostgreSQL

177

Saya memiliki banyak baris yang harus saya masukkan ke dalam tabel, tetapi sisipan ini selalu dilakukan dalam batch. Jadi saya ingin memeriksa apakah satu baris dari batch ada di tabel karena saya tahu mereka semua dimasukkan.

Jadi ini bukan pemeriksaan kunci utama, tetapi seharusnya tidak terlalu penting. Saya hanya ingin memeriksa satu baris jadi count(*)mungkin tidak baik, jadi sepertinya existssaya kira.

Tapi karena saya cukup baru di PostgreSQL saya lebih suka bertanya kepada orang yang tahu.

Batch saya berisi baris dengan struktur berikut:

userid | rightid | remaining_count

Jadi jika tabel berisi baris dengan ketentuan useriditu berarti mereka semua ada di sana.

Valentin Kuzub
sumber
Anda ingin melihat apakah tabel memiliki baris APAPUN, atau baris apa pun dari kumpulan Anda?
JNK
setiap baris dari batch saya ya. mereka semua membagikan sedikit bidang yang sama.
Valentin Kuzub
Tolong jelaskan pertanyaan Anda. Anda ingin menambahkan kumpulan catatan, semuanya atau tidak sama sekali? Apakah ada yang spesial tentang hitungan? (BTW kata yang dipesan, tidak praktis sebagai nama kolom)
wildplasser
oke, saya mencoba menyederhanakan situasi aktual sedikit tetapi kami semakin dekat dan dekat dengan implementasi nyata. Setelah baris-baris itu dimasukkan (ada bidang lain untuk tanggal) saya mulai mengurangi hak untuk pengguna tertentu karena mereka menggunakan hak tertentu, setelah hak menjadi 0 mereka tidak dapat melakukan tindakan itu lagi untuk tanggal tersebut. itulah kisah nyata
Valentin Kuzub
1
Cukup tunjukkan (bagian yang relevan dari) definisi tabel, dan katakan apa yang ingin Anda lakukan.
wildplasser

Jawaban:

345

Gunakan kata kunci EXISTS untuk TRUE / FALSE return:

select exists(select 1 from contact where id=12)
StartupGuy
sumber
21
Ekstensi ini, Anda dapat memberi nama kolom yang dikembalikan untuk referensi mudah. Misalnyaselect exists(select 1 from contact where id=12) AS "exists"
Rowan
3
Ini lebih baik, karena akan selalu mengembalikan nilai (benar atau salah) daripada terkadang Tidak ada (tergantung pada bahasa pemrograman Anda) yang mungkin tidak memperluas cara yang Anda harapkan.
isaaclw
1
Saya memiliki Pemindaian Seq dengan menggunakan metode ini. Saya melakukan sesuatu yang salah?
Lima puluh
2
@ Michael.MI memiliki tabel DB dengan 30 juta baris dan ketika saya menggunakan existsatau limit 1saya memiliki penurunan kinerja yang kuat karena Postgres menggunakan Seq Scan daripada Index Scan. Dan analyzetidak membantu.
FiftiN
2
@maciek tolong mengerti bahwa 'id' adalah kunci utama, jadi "LIMIT 1" tidak ada gunanya karena hanya ada satu catatan dengan id itu
StartupGuy
34

Bagaimana dengan sederhana:

select 1 from tbl where userid = 123 limit 1;

di mana 123userid dari batch yang akan Anda masukkan.

Kueri di atas akan mengembalikan kumpulan kosong atau satu baris, tergantung pada apakah ada catatan dengan userid yang diberikan.

Jika ini ternyata terlalu lambat, Anda bisa melihat membuat indeks tbl.userid.

jika bahkan satu baris dari batch ada dalam tabel, dalam hal ini saya tidak perlu memasukkan baris saya karena saya tahu pasti semuanya dimasukkan.

Agar ini tetap benar bahkan jika program Anda terganggu mid-batch, saya sarankan Anda memastikan Anda mengelola transaksi basis data dengan tepat (yaitu bahwa seluruh batch dimasukkan dalam satu transaksi tunggal).

NPE
sumber
11
Terkadang secara programatis lebih mudah untuk "memilih hitungan (*) dari (pilih 1 ... batas 1)" karena dijamin akan selalu mengembalikan baris dengan nilai hitungan (*) 0 atau 1.
David Aldridge
@DavidAldridge count (*) masih berarti bahwa semua baris harus dibaca, sedangkan batas 1 berhenti pada rekaman pertama dan kembali
Imraan
3
@Imraan Saya pikir Anda salah mengartikan kueri. The COUNTbertindak pada bersarang SELECTyang memiliki paling banyak 1 baris (karena LIMITdalam subquery).
jpmc26
9
INSERT INTO target( userid, rightid, count )
  SELECT userid, rightid, count 
  FROM batch
  WHERE NOT EXISTS (
    SELECT * FROM target t2, batch b2
    WHERE t2.userid = b2.userid
    -- ... other keyfields ...
    )       
    ;

BTW: jika Anda ingin seluruh kumpulan gagal jika duplikat, maka (diberi batasan kunci utama)

INSERT INTO target( userid, rightid, count )
SELECT userid, rightid, count 
FROM batch
    ;

akan melakukan apa yang Anda inginkan: itu berhasil, atau gagal.

wildplasser
sumber
Ini akan memeriksa setiap baris. Dia ingin melakukan pemeriksaan tunggal.
JNK
1
Tidak, itu melakukan pemeriksaan tunggal. Subquery tidak berkorelasi. Ini akan menyelamatkan setelah satu pasangan yang cocok ditemukan.
wildplasser
Benar, saya pikir itu merujuk ke permintaan luar. +1 untuk Anda
JNK
BTW: karena kueri ada di dalam transaksi, tidak akan terjadi apa-apa jika id duplikat dimasukkan, maka subquery dapat dihilangkan.
wildplasser
hmm saya tidak yakin saya mengerti. Setelah hak dimasukkan, saya mulai mengurangi kolom hitung. (hanya beberapa detail untuk gambar) Jika baris sudah ada dan subquery dihilangkan saya pikir saya tidak mendapatkan kesalahan dengan duplikat kunci unik yang dilemparkan atau? (bentuk userid & kanan kunci unik itu)
Valentin Kuzub
1
select true from tablename where condition limit 1;

Saya percaya bahwa ini adalah kueri yang digunakan postgres untuk memeriksa kunci asing.

Dalam kasus Anda, Anda bisa melakukan ini sekaligus:

insert into yourtable select $userid, $rightid, $count where not (select true from yourtable where userid = $userid limit 1);
Royce
sumber
1

seperti yang ditunjukkan @MikeM.

select exists(select 1 from contact where id=12)

dengan indeks pada kontak, biasanya dapat mengurangi biaya waktu menjadi 1 ms.

CREATE INDEX index_contact on contact(id);
hcnak
sumber
0
SELECT 1 FROM user_right where userid = ? LIMIT 1

Jika resultset Anda berisi baris maka Anda tidak perlu memasukkan. Kalau tidak, masukkan catatan Anda.

Fabian Barney
sumber
jika banyak berisi 100 baris itu akan mengembalikan saya 100 baris, Anda pikir itu bagus?
Valentin Kuzub
Anda dapat membatasi hingga 1 baris. Harus berkinerja lebih baik. Lihat jawaban yang sudah diedit dari @aix untuk itu.
Fabian Barney
0

Jika Anda berpikir tentang performace, mungkin Anda dapat menggunakan "PERFORM" dalam suatu fungsi seperti ini:

 PERFORM 1 FROM skytf.test_2 WHERE id=i LIMIT 1;
  IF FOUND THEN
      RAISE NOTICE ' found record id=%', i;  
  ELSE
      RAISE NOTICE ' not found record id=%', i;  
 END IF;
franc
sumber
tidak bekerja dengan saya: Saya mendapatkan kesalahan sintaksis di dekat perform
Simon
1
itu pl / pgsql, bukan SQL, karenanya kesalahan sintaks untuk "PERFORM" jika mencoba menjalankannya sebagai SQL
Mark K Cowan
-1

Saya ingin mengusulkan pikiran lain untuk secara khusus menangani kalimat Anda: "Jadi saya ingin memeriksa apakah satu baris dari batch yang ada di meja karena kemudian saya tahu mereka semua yang dimasukkan ."

Anda membuat hal-hal efisien dengan memasukkan "batch" tetapi kemudian melakukan pemeriksaan keberadaan satu catatan pada suatu waktu? Ini tampaknya kontra intuitif bagi saya. Jadi, ketika Anda mengatakan " sisipan selalu dilakukan dalam batch " saya menganggap Anda berarti Anda memasukkan banyak catatan dengan satu pernyataan insert . Anda perlu menyadari bahwa Postgres sesuai dengan ACID. Jika Anda menyisipkan beberapa catatan (kumpulan data) dengan satu pernyataan masukkan , tidak perlu memeriksa apakah ada yang dimasukkan atau tidak. Pernyataan itu lulus atau akan gagal. Semua catatan akan dimasukkan atau tidak ada.

Di sisi lain, jika kode C # Anda hanya melakukan "set" pernyataan insert yang terpisah, misalnya, dalam satu lingkaran, dan dalam pikiran Anda, ini adalah "batch" .. maka Anda seharusnya tidak menggambarkannya sebagai " sisipan selalu dilakukan dalam batch ". Fakta bahwa Anda mengharapkan bagian dari apa yang Anda sebut "batch", mungkin sebenarnya tidak dimasukkan, dan karenanya merasakan perlunya pemeriksaan, sangat menunjukkan hal ini, dalam hal ini Anda memiliki masalah yang lebih mendasar. Anda perlu mengubah paradigma Anda untuk benar-benar menyisipkan banyak catatan dengan satu sisipan, dan lupakan memeriksa apakah catatan individual membuatnya.

Pertimbangkan contoh ini:

CREATE TABLE temp_test (
    id SERIAL PRIMARY KEY,
    sometext TEXT,
    userid INT,
    somethingtomakeitfail INT unique
)
-- insert a batch of 3 rows
;;
INSERT INTO temp_test (sometext, userid, somethingtomakeitfail) VALUES
('foo', 1, 1),
('bar', 2, 2),
('baz', 3, 3)
;;
-- inspect the data of what we inserted
SELECT * FROM temp_test
;;
-- this entire statement will fail .. no need to check which one made it
INSERT INTO temp_test (sometext, userid, somethingtomakeitfail) VALUES
('foo', 2, 4),
('bar', 2, 5),
('baz', 3, 3)  -- <<--(deliberately simulate a failure)
;;
-- check it ... everything is the same from the last successful insert ..
-- no need to check which records from the 2nd insert may have made it in
SELECT * FROM temp_test

Ini sebenarnya adalah paradigma untuk setiap ACID compliant DB .. bukan hanya Postgresql. Dengan kata lain Anda lebih baik jika Anda memperbaiki konsep "batch" Anda dan menghindari harus melakukan pemeriksaan baris demi baris di tempat pertama.

StartupGuy
sumber