FIND_IN_SET () vs IN ()

125

Saya memiliki 2 tabel di database saya. Satu untuk pesanan, dan satu untuk perusahaan.

Pesanan memiliki struktur ini:

OrderID     |     attachedCompanyIDs
------------------------------------
   1                     1,2,3
   2                     2,4

Dan Perusahaan memiliki struktur ini:

CompanyID      |        name
--------------------------------------
    1                 Company 1
    2                 Another Company
    3                 StackOverflow
    4                 Nothing

Untuk mendapatkan nama perusahaan pesanan, saya dapat melakukan kueri seperti:

SELECT name FROM orders,company
WHERE orderID = 1 AND FIND_IN_SET(companyID, attachedCompanyIDs)

Kueri itu berfungsi dengan baik, tetapi kueri berikut tidak.

SELECT name FROM orders,company
WHERE orderID = 1 AND companyID IN (attachedCompanyIDs)

Mengapa kueri pertama berfungsi tetapi bukan yang kedua?

Kueri pertama kembali:

name
---------------
Company 1
Another Company
StackOverflow

Kueri kedua hanya mengembalikan:

name
---------------
Company 1

Mengapa ini, mengapa permintaan pertama mengembalikan semua perusahaan, tetapi permintaan kedua hanya mengembalikan yang pertama?

Roket Hazmat
sumber
3
attachCompanyIDs adalah satu string besar, jadi mysql mencoba untuk menemukan perusahaan dalam gips untuk integer
Haim Evgi
Saya pikir ini adalah contoh terbaik mysqltutorial.org/mysql-find_in_set
Shurvir Mori

Jawaban:

100
SELECT  name
FROM    orders,company
WHERE   orderID = 1
        AND companyID IN (attachedCompanyIDs)

attachedCompanyIDsadalah nilai skalar yang dilemparkan ke dalam INT(tipe companyID).

Para pemain hanya mengembalikan angka hingga non-digit pertama (koma dalam kasing Anda).

Jadi,

companyID IN ('1,2,3')  companyID IN (CAST('1,2,3' AS INT))  companyID IN (1)

Di PostgreSQL, Anda bisa memasukkan string ke array (atau menyimpannya sebagai array di tempat pertama):

SELECT  name
FROM    orders
JOIN    company
ON      companyID = ANY (('{' | attachedCompanyIDs | '}')::INT[])
WHERE   orderID = 1

dan ini bahkan akan menggunakan indeks companyID.

Sayangnya, ini tidak berfungsi MySQLkarena yang terakhir tidak mendukung array.

Anda mungkin menemukan artikel ini menarik (lihat #2):

Memperbarui:

Jika ada beberapa batasan yang masuk akal pada jumlah nilai dalam daftar yang dipisahkan koma (katakanlah, tidak lebih dari 5), maka Anda dapat mencoba menggunakan kueri ini:

SELECT  name
FROM    orders
CROSS JOIN
        (
        SELECT  1 AS pos
        UNION ALL
        SELECT  2 AS pos
        UNION ALL
        SELECT  3 AS pos
        UNION ALL
        SELECT  4 AS pos
        UNION ALL
        SELECT  5 AS pos
        ) q
JOIN    company
ON      companyID = CAST(NULLIF(SUBSTRING_INDEX(attachedCompanyIDs, ',', -pos), SUBSTRING_INDEX(attachedCompanyIDs, ',', 1 - pos)) AS UNSIGNED)
Quassnoi
sumber
3
Terima kasih untuk penjelasannya. Saya tidak menyadari bidang terlampirCompanyIDs dilemparkan ke INT. Apakah ada cara lain di MySQL? FIND_IN_SETberfungsi, tetapi tidak menggunakan indeks, dan mungkin lambat dengan banyak info di tabel Perusahaan.
Rocket Hazmat
1
Bisakah Anda menjelaskan pembaruan itu? Apa sebenarnya yang dilakukan, karena tampaknya berhasil.
Rocket Hazmat
1
@Rocket: ini menghapus positem dari awal CVSdan melemparkan sisanya ke integer.
Quassnoi
9
Acungan jempol (y) untuk10 things in MySQL (that won’t work as expected)
NullPointer
@Quassnoi, Kenapa kamu menulis CROSS JOIN? Bukankah semuanya sama di MySQL?
Pacerier
13

attachCompanyIDs adalah satu string besar, jadi mysql mencoba untuk menemukan perusahaan dalam gips untuk integer

ketika Anda gunakan di mana

jadi jika comapnyid = 1:

companyID IN ('1,2,3')

ini benar kembali

tetapi jika nomor 1 tidak di tempat pertama

 companyID IN ('2,3,1')

pengembaliannya salah

Haim Evgi
sumber
3

Untuk mendapatkan nama semua perusahaan terkait, tidak didasarkan pada ID tertentu.

SELECT 
    (SELECT GROUP_CONCAT(cmp.cmpny_name) 
    FROM company cmp 
    WHERE FIND_IN_SET(cmp.CompanyID, odr.attachedCompanyIDs)
    ) AS COMPANIES
FROM orders odr
Anupriya Pundir
sumber
1

karena kueri kedua mencari baris dengan id 1 ATAU 2 ATAU 3, kueri pertama mencari salah satu dari nilai yang dibatasi koma yang ada di companyID,

dan masalah lain di sini adalah Anda tidak bergabung dengan tabel pada kunci umum di mana Anda sehingga Anda akan mendapatkan mutasi baris yang = count (table1) * count (table2);

Masalah Anda benar-benar ada pada bagian 2 dari jawaban saya. (dengan permintaan kedua Anda)

superfro
sumber
Ada lebih banyak baris di kedua tabel daripada yang saya tunjukkan. Di kedua tabel, ada ID pengguna tempat Anda masuk, akan bergabung dengan bantuan itu?
Rocket Hazmat
Yah, Anda hanya perlu mengubah apa pun jika permintaan pertama Anda tidak mengembalikan hasil yang diharapkan. Jika kueri pertama mengembalikan hasil yang Anda inginkan maka sebenarnya tidak ada masalah. Saya pikir Anda hanya ingin tahu mengapa 2 tidak menunjukkan hasil yang sama.
superfro
@superfro, saya ingin tahu mengapa 2 tidak menunjukkan hasil yang sama.
Rocket Hazmat
kueri kedua Anda menggunakan di mana IN (nilai) tempat bagian 'nilai' berasal dari tabel, dan sebuah string. String sedang dievaluasi sebagai bool true yang = 1 itulah sebabnya hanya menunjukkan baris pertama.
superfro
1
Jika Anda khawatir tentang kinerja, Anda mungkin harus berpikir tentang mengubah struktur database Anda. Anda bisa menambahkan tabel bersama yang berisi 2 nilai, order_ID dan company_ID alih-alih menggunakan daftar yang dibatasi koma di tabel pesanan. Ini akan memungkinkan Anda untuk memilih nama dari perusahaan yang tersisa, bergabung dengan order_companies di company.company_ID = order_companies.company_ID, meninggalkan pesanan bergabung di order_companies.order_ID = order.order_ID di mana order.order_ID = 1; Ini akan menggunakan indeks.
superfro
-1

Biarkan saya menjelaskan kapan harus menggunakan FIND_IN_SET dan Kapan menggunakan IN.

Mari kita ambil tabel A yang memiliki kolom bernama "aid", "aname". Mari kita ambil tabel B yang memiliki kolom bernama "bid", "bname", "aids".

Sekarang ada nilai dummy di Tabel A dan Tabel B seperti di bawah ini.

Tabel A

aname bantuan

1 Apple

2 pisang

3 Mangga

Tabel B

bantu tawaran bname

1 Apple 1,2

2 Pisang 2,1

3 Mangga 3,1,2

enter code here

Kasus1: jika Anda ingin mendapatkan catatan itu dari tabel b yang memiliki 1 nilai hadir di kolom bantu maka Anda harus menggunakan FIND_IN_SET.

Kueri: pilih * dari A JOIN B ON FIND_IN_SET (A.aid, b.aids) di mana A.aid = 1;

Kasus2: jika Anda ingin mendapatkan catatan-catatan dari tabel a yang memiliki nilai 1 ATAU 2 ATAU 3 hadir di kolom bantuan maka Anda harus menggunakan IN.

Pertanyaan: pilih * dari A JOIN B ON A.aid IN (b.aids);

Sekarang terserah Anda bahwa apa yang Anda butuhkan melalui permintaan mysql.

pemalu
sumber
Pertanyaan ini sudah dipecahkan. Saya juga tidak berpikir contoh kedua Anda, dengan IN, berfungsi ... itu pada dasarnya masalah yang saya coba pecahkan pada awalnya.
Rocket Hazmat
-2
SELECT o.*, GROUP_CONCAT(c.name) FROM Orders AS o , Company.c
    WHERE FIND_IN_SET(c.CompanyID , o.attachedCompanyIDs) GROUP BY o.attachedCompanyIDs
amit gangrade
sumber
6
Selamat datang di SO! Kode tanpa penjelasan jarang membantu. Dalam hal ini bahkan tidak mencoba menjawab pertanyaan "Kenapa ...?". Harap perhatikan juga bahwa pertanyaan khusus ini sudah memiliki jawaban yang diterima yang memberikan jawaban yang diterima dengan baik (> 80 suara!). Sebagai pengguna baru mungkin lebih baik untuk fokus pada pertanyaan yang tidak dijawab dan / atau mengajukan pertanyaan yang baik sendiri.
cfi