FIND_IN_SET()とIN()


125

データベースに2つのテーブルがあります。1つは注文用、もう1つは会社用です。

Ordersの構造は次のとおりです。

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

そして会社はこの構造を持っています:

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

注文の会社名を取得するには、次のようにクエリを実行します。

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

そのクエリは正常に機能しますが、次のクエリは機能しません。

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

最初のクエリは機能するが、2番目のクエリは機能しないのはなぜですか?

最初のクエリは次を返します:

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

2番目のクエリは次のもののみを返します。

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

これはなぜですか、なぜ最初のクエリはすべての会社を返しますが、2番目のクエリは最初のクエリのみを返すのですか?


3
attachedCompanyIDsは1つの大きな文字列なので、mysqlは
これから

回答:


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

attachedCompanyIDsINT(型companyID)にキャストされるスカラー値です。

キャストでは、最初の数字以外の数値(大文字の場合はカンマ)のみが返されます。

したがって、

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

ではPostgreSQL、文字列を配列にキャストできます(または最初に配列として保存できます)。

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

これはのインデックスも使用しますcompanyID

MySQL後者は配列をサポートしていないため、残念ながらこれは機能しません。

この記事は興味深いかもしれません(を参照#2)。

更新:

コンマ区切りのリストの値の数に妥当な制限がある場合(たとえば、以下5)、このクエリを使用してみることができます。

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
説明ありがとう。attachedCompanyIDsフィールドがINTにキャストされることに気付きませんでした。MySQLでこれを回避する方法はありますか? FIND_IN_SET動作しますが、インデックスを使用しません。また、Companyテーブルに多くの情報があるために遅くなる可能性があります。
Rocket Hazmat

1
その更新について説明できますか?それが機能しているように見えるので、それは正確に何をしますか。
ロケットHazmat

1
@Rocket:posの先頭からアイテムを取り除きCVS、残りを整数にキャストします。
Quassnoi

9
いいね(y)10 things in MySQL (that won’t work as expected)
NullPointer

@Quassnoi、なぜあなたは書くのCROSS JOINですか?MySQL ではすべて同じではありませんか?
Pacerier、2015

13

attachedCompanyIDsは1つの大きな文字列なので、mysqlはこれを整数にキャストして会社を見つけようとします

どこで使うか

したがって、comapnyid = 1の場合:

companyID IN ('1,2,3')

これは真実を返す

しかし、最初の数が1でない場合

 companyID IN ('2,3,1')

その戻り値はfalse


3

特定のIDに基づくのではなく、すべての関連会社名を取得する。

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

1

2番目のクエリは、IDが1 OR 2 OR 3の行を探しているため、最初のクエリは、companyIDに存在するカンマ区切り値の1つを探しています。

ここでのもう1つの問題は、whereの共通キーでテーブルを結合していないため、次のような行の変更を取得することになります= count(table1)* count(table2);

あなたの問題は本当に私の答えのパート2に存在します。(2番目のクエリを使用)


両方のテーブルに、表示しているよりも多くの行があります。どちらの表にも、ログインしているユーザーのIDがあります。そのヘルプに参加しますか?
Rocket Hazmat

最初のクエリが期待した結果を返さない場合のみ、変更する必要があります。最初のクエリが必要な結果を返している場合、問題はありません。2が同じ結果を示さない理由を知りたがっていただけだと思いました。
superfro

@superfro、なぜ2が同じ結果を示さないのか知りたいです。
ロケットHazmat

2番目のクエリは、 '値'の部分がテーブルにあるwhere IN(値)とその文字列を使用しています。文字列はbool true which = 1として評価されているため、最初の行のみが表示されます。
superfro 2010

1
パフォーマンスが心配な場合は、データベース構造の変更を検討する必要があります。オーダーテーブルでコンマ区切りのリストを使用する代わりに、order_IDとcompany_IDの2つの値を含む結合テーブルを追加できます。これにより、company.company_ID = order_companies.company_IDの左の結合join_companiesから会社の名前を選択できるようになります。これはインデックスを使用します。
superfro 2010

-1

FIND_IN_SETを使用する場合とINを使用する場合について説明します。

「aid」、「aname」という名前の列を持つテーブルAを見てみましょう。「bid」、「bname」、「aids」という名前の列を持つテーブルBを見てみましょう。

これで、表Aと表Bに次のようなダミー値があります。

テーブルA

名前を助ける

1リンゴ

2バナナ

3マンゴー

表B

応急手当

1アップル1,2

2バナナ2,1

3マンゴー3,1,2

enter code here

Case1:エイズ列に1つの値が存在するテーブルbからこれらのレコードを取得する場合は、FIND_IN_SETを使用する必要があります。

クエリ:select * from A JOIN B ON FIND_IN_SET(A.aid、b.aids)where A.aid = 1;

ケース2:エイド列に1 OR 2 OR 3の値が存在するテーブルaからこれらのレコードを取得する場合は、INを使用する必要があります。

クエリ:select * from A JOIN B ON A.aid IN(b.aids);

ここで、mysqlクエリを介して必要なものをここまでです。


この質問はすでに解決されています。また、INを使用した2番目の例が機能するとは思わない...それは、基本的に私が最初に解決しようとしていた問題でした。
ロケットハズマット2017

-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へようこそ!説明のないコードが役立つことはほとんどありません。この場合、「なぜ...」という質問に答えようとはしません。また、この特定の質問には、よく受け入れられた(> 80票!)回答を与える承認済みの回答があることに注意してください。新規ユーザーとしては、未回答の質問に集中したり、自分で良い質問をしたりするのが最善の場合があります。
cfi 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.