FIND_IN_SET () - IN ()


125

Veritabanımda 2 tablom var. Biri siparişler için, diğeri şirketler içindir.

Siparişler şu yapıya sahiptir:

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

Ve Şirket şu yapıya sahiptir:

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

Bir siparişin şirket adlarını almak için şu şekilde bir sorgu yapabilirim:

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

Bu sorgu iyi çalışıyor, ancak aşağıdaki sorgu çalışmıyor.

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

Neden ilk sorgu çalışıyor ama ikincisi çalışmıyor?

İlk sorgu şunu döndürür:

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

İkinci sorgu yalnızca şunu döndürür:

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

Neden bu, neden ilk sorgu tüm şirketleri döndürürken, ikinci sorgu yalnızca ilkini döndürür?


3
attachCompanyIDs büyük bir dizedir, bu yüzden mysql bu konuda şirket bulmaya çalışın, tam sayıya dönüştürülür
Haim Evgi

Bunun en iyi örnek olduğunu düşünüyorum mysqltutorial.org/mysql-find_in_set
Shurvir Mori

Yanıtlar:


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

attachedCompanyIDsINT(type companyID) 'a dönüştürülen skaler bir değerdir .

Oyuncular yalnızca ilk basamağa kadar olan sayıları döndürür (sizin durumunuzda virgül).

Böylece,

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

İçinde PostgreSQLdizeyi diziye çevirebilir (veya ilk etapta bir dizi olarak saklayabilirsiniz):

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

ve bu bir dizin bile kullanırdı companyID.

Ne yazık ki, MySQLikincisi dizileri desteklemediği için bu işe yaramıyor.

Bu makaleyi ilginç bulabilirsiniz (bakınız #2):

Güncelleme:

Virgülle ayrılmış listelerdeki değerlerin sayısında makul bir sınır varsa (diyelim, en fazla 5), bu sorguyu kullanmayı deneyebilirsiniz:

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)

3
Açıklama için teşekkürler. EkliCompanyIDs alanının bir INT'ye dönüştürüldüğünü fark etmedim. MySQL'de bunun etrafında herhangi bir yol var mı? FIND_IN_SETçalışır, ancak dizinler kullanmaz ve Şirket tablosundaki birçok bilgi nedeniyle yavaş olabilir.
Rocket Hazmat

1
Bu güncellemeyi açıklayabilir misin? Bu tam olarak ne yapıyor, çünkü işe yarıyor gibi görünüyor.
Rocket Hazmat

1
@Rocket: posÖğeleri başından itibaren çıkarır CVSve geri kalanını tam sayıya çevirir .
Quassnoi

9
İçin başparmak yukarı (y)10 things in MySQL (that won’t work as expected)
NullPointer

@Quassnoi, Neden yazıyorsun CROSS JOIN? MySQL'de hepsi aynı değil mi?
Pacerier

13

attachCompanyIDs tek bir büyük dizedir, bu nedenle mysql tam sayıya dönüştürülen bunda şirket bulmaya çalışın

nerede kullanılırsa

comapnyid = 1 ise:

companyID IN ('1,2,3')

bu doğru dönüş

ama 1 numara ilk sırada değilse

 companyID IN ('2,3,1')

yanlış dönüşü


3

Belirli bir kimliğe göre değil, tüm ilgili şirketlerin adını almak için.

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

1

İkinci sorgu, 1 VEYA 2 VEYA 3 kimliğine sahip satırları aradığı için, ilk sorgu, şirket kimliğinde var olan virgülle ayrılmış değerlerden birini arıyor,

ve buradaki başka bir sorun da, masalarınıza ortak bir anahtarda katılmıyorsunuz, böylece = sayı (tablo1) * sayım (tablo2) olan satırların bir mutasyonunu alacaksınız;

Sorunun cevabımın 2. bölümünde gerçekten var. (ikinci sorgunuzla)


Her iki tabloda da gösterdiğimden daha fazla satır var. Her iki tabloda da, giriş yaptığınız kullanıcının kimliği var, bu yardıma katılmak ister misiniz?
Rocket Hazmat

İlk sorgunuz beklenen sonuçları döndürmüyorsa yalnızca herhangi bir şeyi değiştirmeniz gerekir. İlk sorgu istediğiniz sonuçları döndürüyorsa, o zaman gerçekten bir sorun yoktur. Ben sadece 2'nin neden aynı sonucu göstermediğini merak ettiğinizi sanıyordum.
superfro

@superfro, 2'nin neden aynı sonucu göstermediğini merak ediyorum.
Rocket Hazmat

ikinci sorgunuz, 'değerler' kısmının tablodan geldiği bir IN (değerler) ve onun bir dizesi kullanıyor. Dize bir bool true olarak değerlendiriliyor, bu da = 1, bu yüzden sadece ilk satırı gösteriyor.
superfro

1
Performans konusunda endişeleriniz varsa, muhtemelen veritabanı yapınızı değiştirmeyi düşünmelisiniz. Sipariş tablosunda virgülle ayrılmış liste kullanmak yerine 2 değer, order_ID ve company_ID içeren bir ortak tablo ekleyebilirsiniz. Bu, company.company_ID = order_companies.company_ID'de sol şirketten katılma order_companies'ten ad seçmenize olanak tanır; order_companies.order_ID = order.order_ID'de siparişler.order_ID = 1; Bu, dizinleri kullanır.
superfro

-1

Ne zaman FIND_IN_SET ve ne zaman IN kullanılacağını açıklayayım.

"Yardımcı", "aname" adlı sütunları olan A tablosunu ele alalım. "Bid", "bname", "aids" adlı sütunları olan B tablosunu alalım.

Şimdi Tablo A ve Tablo B'de aşağıdaki gibi yapay değerler vardır.

Tablo A

aname yardım

1 elma

2 Muz

3 Mango

Tablo B

teklif teklif adı yardımları

1 Elma 1,2

2 Muz 2,1

3 Mango 3,1,2

enter code here

Durum 1: Bu kayıtları AIDS sütunlarında 1 değeri bulunan tablo b'den almak istiyorsanız, FIND_IN_SET kullanmanız gerekir.

Sorgu: A.aid = 1 olan FIND_IN_SET (A.aid, b.aids) üzerindeki A JOIN B'den * seçin;

Durum 2: Bu kayıtları, yardım sütunlarında 1 VEYA 2 VEYA 3 değeri bulunan tablo a'dan almak istiyorsanız, IN kullanmanız gerekir.

Sorgu: A JOIN B ON A.aid IN'den * seçin (b.aids);

Şimdi burada size mysql sorgusu aracılığıyla ihtiyacınız olan şey.


Bu soru zaten çözüldü. Ayrıca IN ile olan ikinci örneğinizin işe yaradığını düşünmüyorum ... temelde başlangıçta çözmeye çalıştığım problem buydu.
Roket Tehlikeli madde

-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

6
SO'ya hoş geldiniz! Açıklamasız kod nadiren yardımcı olur. Bu durumda "Neden ...?" Sorusuna bile cevap vermeye çalışmaz. Ayrıca, bu özel sorunun halihazırda kabul edilmiş bir cevaba sahip olduğunu ve iyi alınmış (> 80 oy!) Bir cevap verdiğini lütfen unutmayın. Yeni bir kullanıcı olarak cevaplanmamış sorulara odaklanmak ve / veya kendiniz iyi sorular sormak en iyisi olabilir.
cfi
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.