FIND_IN_SET () เทียบกับ IN ()


125

ฉันมี 2 ตารางในฐานข้อมูลของฉัน หนึ่งคือสำหรับการสั่งซื้อและอีกรายการสำหรับ บริษัท

คำสั่งซื้อมีโครงสร้างดังนี้

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)

เหตุใดแบบสอบถามแรกจึงใช้งานได้ แต่ไม่ใช่แบบสอบถามที่สอง

แบบสอบถามแรกส่งกลับ:

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

แบบสอบถามที่สองส่งกลับเท่านั้น:

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

เหตุใดจึงเป็นเช่นนี้เหตุใดแบบสอบถามแรกจึงส่งคืน บริษัท ทั้งหมด แต่แบบสอบถามที่สองส่งคืนเฉพาะคำค้นหาแรกเท่านั้น


3
attachedCompanyIDs เป็นสตริงขนาดใหญ่ดังนั้น mysql จึงพยายามหา บริษัท ในนี้ให้เป็นจำนวนเต็ม
Haim Evgi

ฉันคิดว่านี่เป็นตัวอย่างที่ดีที่สุดmysqltutorial.org/mysql-find_in_set
Shurvir Mori

คำตอบ:


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

attachedCompanyIDsคือค่าสเกลาร์ที่ร่ายเป็นINT(ประเภท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
ขอบคุณสำหรับคำอธิบาย ฉันไม่ทราบว่าฟิลด์ที่แนบมาด้วยรหัส บริษัท ถูกส่งไปยัง INT มีวิธีแก้ปัญหานี้ใน MySQL หรือไม่? FIND_IN_SETใช้งานได้ แต่ไม่ใช้ดัชนีและอาจทำงานช้าเมื่อมีข้อมูลจำนวนมากในตาราง บริษัท
Rocket Hazmat

1
คุณสามารถอธิบายการอัปเดตนั้นได้หรือไม่? มันทำอะไรกันแน่เพราะดูเหมือนว่าจะได้ผล
Rocket Hazmat

1
@Rocket: มันดึงposรายการจากจุดเริ่มต้นของCVSและโยนส่วนที่เหลือเป็นจำนวนเต็ม
Quassnoi

9
ยกนิ้วให้ (y) สำหรับ10 things in MySQL (that won’t work as expected)
NullPointer

@ Quassnoi คุณเขียนCROSS JOINทำไม? พวกเขาไม่เหมือนกันทั้งหมดใน MySQL หรือไม่?
Pacerier

13

attachedCompanyIDs เป็นสตริงขนาดใหญ่หนึ่งสตริงดังนั้น mysql จึงพยายามหา บริษัท ในการแคสต์เป็นจำนวนเต็ม

เมื่อคุณใช้ที่ไหนใน

ดังนั้นถ้า comapnyid = 1:

companyID IN ('1,2,3')

นี่คือผลตอบแทนจริง

แต่ถ้าหมายเลข 1 ไม่ได้อยู่ในอันดับแรก

 companyID IN ('2,3,1')

กลับเป็นเท็จ


3

เพื่อให้ได้ชื่อ บริษัท ที่เกี่ยวข้องทั้งหมดโดยไม่อิงตามรหัสเฉพาะ

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

1

เนื่องจากแบบสอบถามที่สองกำลังค้นหาแถวที่มี 1 หรือ 2 หรือ 3 ของ id แบบสอบถามแรกกำลังมองหาค่าที่คั่นด้วยจุลภาคค่าหนึ่งที่มีอยู่ใน companyID

และปัญหาอีกประการหนึ่งคือคุณไม่ได้เข้าร่วมตารางบนคีย์ทั่วไปในตำแหน่งของคุณดังนั้นคุณจะได้รับการกลายพันธุ์ของแถวที่ = count (table1) * count (table2);

ปัญหาของคุณมีอยู่จริงในส่วนที่ 2 ของคำตอบของฉัน (ด้วยคำถามที่สองของคุณ)


มีแถวในทั้งสองตารางมากกว่าที่ฉันแสดง ในทั้งสองตารางมี ID ของผู้ใช้ที่คุณเข้าสู่ระบบด้วยจะเข้าร่วมในความช่วยเหลือนั้นหรือไม่
Rocket Hazmat

คุณจะต้องเปลี่ยนแปลงอะไรก็ต่อเมื่อแบบสอบถามแรกของคุณไม่ได้ผลลัพธ์ที่คาดหวัง หากแบบสอบถามแรกส่งคืนผลลัพธ์ที่คุณต้องการแสดงว่าไม่มีปัญหาจริงๆ ฉันคิดว่าคุณแค่สงสัยว่าทำไมทั้ง 2 ไม่แสดงผลลัพธ์เหมือนกัน
superfro

@superfro ฉันอยากรู้ว่าทำไมทั้ง 2 ไม่แสดงผลลัพธ์เหมือนกัน
Rocket Hazmat

แบบสอบถามที่สองของคุณใช้โดยที่ IN (ค่า) โดยที่ส่วน "ค่า" มาจากตารางและสตริง สตริงกำลังได้รับการประเมินเป็นบูลจริงซึ่ง = 1 ซึ่งเป็นสาเหตุที่แสดงเฉพาะแถวแรก
superfro

1
หากคุณกังวลเกี่ยวกับประสิทธิภาพคุณควรคิดถึงการเปลี่ยนแปลงโครงสร้างฐานข้อมูลของคุณ คุณสามารถเพิ่มตารางร่วมที่มี 2 ค่าคือ order_ID และ company_ID แทนที่จะใช้รายการที่คั่นด้วยจุลภาคในตารางคำสั่งซื้อ สิ่งนี้จะช่วยให้คุณสามารถเลือกชื่อจาก บริษัท ที่เหลือเข้าร่วม order_companies บน company.company_ID = order_companies.company_ID ออกจากคำสั่งเข้าร่วมใน order_companies.order_ID = order.order_ID โดยที่ orders.order_ID = 1; สิ่งนี้จะใช้ดัชนี
superfro

-1

ให้ฉันอธิบายว่าเมื่อใดควรใช้ FIND_IN_SET และเมื่อใดควรใช้ใน

มาดูตาราง A ซึ่งมีคอลัมน์ชื่อ "aid", "aname" มาดูตาราง B ซึ่งมีคอลัมน์ชื่อ "bid", "bname", "aids"

ตอนนี้มีค่าดัมมี่ในตาราง A และตาราง B ดังต่อไปนี้

ตารางก

ช่วยเหลือ aname

1 แอปเปิ้ล

2 กล้วย

มะม่วง 3 ลูก

ตาราง B

เสนอราคา bname ช่วย

1 แอปเปิ้ล 1,2

กล้วย 2 ลูก 2,1

มะม่วง 3 ลูก 3,1,2

enter code here

กรณีที่ 1: ถ้าคุณต้องการรับระเบียนเหล่านั้นจากตาราง b ซึ่งมีค่า 1 ค่าอยู่ในคอลัมน์เอดส์คุณต้องใช้ FIND_IN_SET

แบบสอบถาม: เลือก * จาก A JOIN B ON FIND_IN_SET (A.aid, b.aids) โดยที่ A.aid = 1;

Case2: ถ้าคุณต้องการรับระเบียนเหล่านั้นจากตารางที่มีค่า 1 หรือ 2 หรือ 3 อยู่ในคอลัมน์ช่วยเหลือคุณต้องใช้ IN

แบบสอบถาม: เลือก * จาก A JOIN B ON A.aid IN (b.aids);

ตอนนี้ถึงคุณแล้วว่าคุณต้องการอะไรผ่านแบบสอบถาม mysql


คำถามนี้ได้รับการแก้ไขแล้ว นอกจากนี้ฉันไม่คิดว่าตัวอย่างที่สองของคุณกับ IN ได้ผล ... นั่นเป็นปัญหาที่ฉันพยายามแก้ไขในตอนแรก
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

6
ยินดีต้อนรับสู่ SO! รหัสที่ไม่มีคำอธิบายแทบจะไม่มีประโยชน์ ในกรณีนี้มันไม่ได้พยายามที่จะตอบคำถาม "ทำไม ... ?" นอกจากนี้โปรดทราบว่าคำถามนี้มีคำตอบที่ได้รับการยอมรับซึ่งให้คำตอบที่ได้รับการตอบรับเป็นอย่างดี (> 80 คะแนน!) ในฐานะผู้ใช้ใหม่คุณควรมุ่งเน้นไปที่คำถามที่ยังไม่มีคำตอบและ / หรือถามคำถามดีๆด้วยตัวเอง
cfi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.