مطابقة SQL من كثير إلى كثير
-
09-06-2019 - |
سؤال
أقوم بتنفيذ نظام وضع العلامات لموقع ويب.هناك علامات متعددة لكل كائن وكائنات متعددة لكل علامة.ويتم تحقيق ذلك عن طريق الاحتفاظ بجدول يحتوي على قيمتين لكل سجل، واحدة لمعرفات الكائن والعلامة.
إنني أتطلع إلى كتابة استعلام للعثور على الكائنات التي تطابق مجموعة معينة من العلامات.لنفترض أن لدي البيانات التالية (بتنسيق [object] -> [tags]*)
apple -> fruit red food
banana -> fruit yellow food
cheese -> yellow food
firetruck -> vehicle red
إذا كنت أرغب في مطابقة (الأحمر)، فيجب أن أحصل على تفاحة وعربة إطفاء.إذا كنت أرغب في مطابقة (الفاكهة، الطعام) فيجب أن أحصل على (التفاح، الموز).
كيف أكتب استعلام SQL أفعل ما أريد؟
@ جيريمي روتين،
شكرا لإجابتك.تم استخدام الترميز المستخدم لإعطاء بعض نماذج البيانات - تحتوي قاعدة البيانات الخاصة بي على جدول بمعرف كائن واحد وعلامة واحدة لكل سجل.
ثانيًا، مشكلتي هي أنني بحاجة إلى الحصول على جميع الكائنات التي تطابق جميع العلامات.استبدال OR الخاص بك بـ AND مثل ذلك:
SELECT object WHERE tag = 'fruit' AND tag = 'food';
لا يعطي أي نتائج عند التشغيل.
المحلول
منح:
- جدول الكائنات (معرف المفتاح الأساسي)
- جدول علامات الكائنات (مفاتيح خارجية objectId، tagid)
جدول العلامات (معرف المفتاح الأساسي)
SELECT distinct o.* from object o join objecttags ot on o.Id = ot.objectid join tags t on ot.tagid = t.id where t.Name = 'fruit' or t.name = 'food';
يبدو هذا معكوسًا، لأنك تريد ذلك، ولكن المشكلة هي أن علامتين ليسا في نفس الصف، وبالتالي، لا ينتج أي شيء، نظرًا لأن صفًا واحدًا لا يمكن أن يكون فاكهة وطعامًا في نفس الوقت.سينتج عن هذا الاستعلام تكرارات عادةً، لأنك ستحصل على صف واحد من كل كائن لكل علامة.
إذا كنت ترغب حقًا في القيام بذلك وفي هذه الحالة، فستحتاج إلى group by
, ، و أ having count = <number of ors>
في استفسارك على سبيل المثال.
SELECT distinct o.name, count(*) as count
from object o join objecttags ot on o.Id = ot.objectid
join tags t on ot.tagid = t.id
where t.Name = 'fruit' or t.name = 'food'
group by o.name
having count = 2;
نصائح أخرى
يا إلهي ربما أخطأت في تفسير تعليقك الأصلي.
أسهل طريقة للقيام بذلك في SQL هي أن يكون لديك ثلاثة جداول:
1) Tags ( tag_id, name )
2) Objects (whatever that is)
3) Object_Tag( tag_id, object_id )
وبعد ذلك، يمكنك طرح أي سؤال تريده بشأن البيانات بسرعة وسهولة وكفاءة (شريطة أن تقوم بالفهرسة بشكل مناسب).إذا كنت تريد أن تكون مميزًا، فيمكنك السماح بالعلامات متعددة الكلمات أيضًا (هناك طريقة أنيقة، وطريقة أقل أناقة، على ما أعتقد).
أفترض أن هذا هو ما لديك، لذلك سيعمل SQL أدناه:
الطريقة الحرفية:
SELECT obj
FROM object
WHERE EXISTS( SELECT *
FROM tags
WHERE tag = 'fruit'
AND oid = object_id )
AND EXISTS( SELECT *
FROM tags
WHERE tag = 'Apple'
AND oid = object_id )
هناك أيضًا طرق أخرى يمكنك القيام بها، مثل:
SELECT oid
FROM tags
WHERE tag = 'Apple'
INTERSECT
SELECT oid
FROM tags
WHERE tag = 'Fruit'
@ كايل:يجب أن يكون استعلامك أشبه بما يلي:
SELECT object WHERE tag IN ('fruit', 'food');
كان استعلامك يبحث عن صفوف حيث كانت العلامة عبارة عن فاكهة وطعام، وهو أمر مستحيل رؤيته لأن الحقل يمكن أن يحتوي على قيمة واحدة فقط، وليس كلاهما في نفس الوقت.
اجمع بين اقتراح Steve M. واقتراح Jeremy، وستحصل على سجل واحد يحتوي على ما تبحث عنه:
select object
from tblTags
where tag = @firstMatch
and (
@secondMatch is null
or
(object in (select object from tblTags where tag = @secondMatch)
)
الآن، هذا لا يتسع بشكل جيد ولكنه سيحصل على ما تبحث عنه.أعتقد أن هناك طريقة أفضل للقيام بذلك حتى تتمكن بسهولة من الحصول على عدد N من العناصر المطابقة دون تأثير كبير على الكود ولكنه يفلت مني حاليًا.
أوصي بالمخطط التالي.
Objects: objectID, objectName
Tags: tagID, tagName
ObjectTag: objectID,tagID
مع الاستعلام التالي.
select distinct
objectName
from
ObjectTab ot
join object o
on o.objectID = ot.objectID
join tabs t
on t.tagID = ot.tagID
where
tagName in ('red','fruit')
أقترح أن يحتوي الجدول الخاص بك على علامة واحدة لكل سجل، مثل هذا:
apple -> fruit
apple -> red
apple -> food
banana -> fruit
banana -> yellow
banana -> food
ثم يمكنك فقط
SELECT object WHERE tag = 'fruit' OR tag = 'food';
إذا كنت تريد حقًا القيام بذلك بطريقتك، فيمكنك القيام بذلك على النحو التالي:
SELECT object WHERE tag LIKE 'red' OR tag LIKE '% red' OR tag LIKE 'red %' OR tag LIKE '% red %';