अगर user_resources
(t1) प्रत्येक user => resource
. के लिए एक पंक्ति के साथ एक 'सामान्यीकृत तालिका' थी संयोजन तो उत्तर पाने के लिए क्वेरी उतनी ही सरल होगी जितनी कि joining
टेबल एक साथ।
काश, यह denormalized
है resources
. होने से कॉलम के रूप में:'संसाधन आईडी की सूची' को ';' से अलग किया गया चरित्र।
यदि हम 'संसाधन' कॉलम को पंक्तियों में बदल सकते हैं तो बहुत सी कठिनाइयाँ दूर हो जाती हैं क्योंकि टेबल जुड़ना आसान हो जाता है।
माना गया आउटपुट जेनरेट करने के लिए क्वेरी:
SELECT user_resource.user,
resource.data
FROM user_resource
JOIN integerseries AS isequence
ON isequence.id <= COUNT_IN_SET(user_resource.resources, ';') /* normalize */
JOIN resource
ON resource.id = VALUE_IN_SET(user_resource.resources, ';', isequence.id)
ORDER BY
user_resource.user, resource.data
आउटपुट:
user data
---------- --------
sampleuser abcde
sampleuser azerty
sampleuser qwerty
stacky qwerty
testuser abcde
testuser azerty
कैसे:
'चाल' एक ऐसी तालिका है जिसमें 1 से लेकर कुछ सीमा तक की संख्याएँ हों। मैं इसे integerseries
कहता हूं . इसका उपयोग 'क्षैतिज' चीजों को बदलने के लिए किया जा सकता है जैसे:';' delimited strings
rows
. में .
इसके काम करने का तरीका यह है कि जब आप integerseries
. के साथ 'शामिल हों' , आप एक cross join
कर रहे हैं , जो 'स्वाभाविक रूप से' 'इनर जॉइन' के साथ होता है।
प्रत्येक पंक्ति integerseries
. से भिन्न 'अनुक्रम संख्या' के साथ दोहराई जाती है तालिका जिसे हम उस सूची में 'संसाधन' के 'सूचकांक' के रूप में उपयोग करते हैं जिसे हम उस row
के लिए उपयोग करना चाहते हैं ।
यह विचार है:
- सूची में मदों की संख्या गिनें।
- सूची में अपनी स्थिति के आधार पर प्रत्येक आइटम को निकालें।
integerseries
का उपयोग करें एक पंक्ति कोuser
. से व्यक्तिगत 'संसाधन आईडी' निकालने वाली पंक्तियों के समूह में बदलने के लिए .resources
जैसे-जैसे हम आगे बढ़ते हैं।
मैंने दो कार्यों का उपयोग करने का निर्णय लिया:
-
फ़ंक्शन जिसने 'सीमांकित स्ट्रिंग सूची' और 'इंडेक्स' दिया है, सूची में स्थिति पर मान वापस कर देगा। मैं इसे कॉल करता हूं:
VALUE_IN_SET
. यानी 'ए;बी;सी' और 2 का 'इंडेक्स' दिया गया तो यह 'बी' लौटाता है। -
फ़ंक्शन जिसने 'सीमांकित स्ट्रिंग सूची' दी है, सूची में आइटम्स की संख्या की गिनती लौटाएगा। मैं इसे कॉल करता हूं:
COUNT_IN_SET
. यानी दिया गया 'ए;बी;सी' 3 लौटाएगा
यह पता चला है कि वे दो कार्य और integerseries
delimited items list in a column
।
क्या यह काम करता है?
';' delimited string in column
. यह 'cross_join' (isequence.id
के कारण जेनरेट किए गए मानों सहित सभी कॉलम दिखाता है। resources_index
. के रूप में ):
SELECT user_resource.user,
user_resource.resources,
COUNT_IN_SET(user_resource.resources, ';') AS resources_count,
isequence.id AS resources_index,
VALUE_IN_SET(user_resource.resources, ';', isequence.id) AS resources_value
FROM
user_resource
JOIN integerseries AS isequence
ON isequence.id <= COUNT_IN_SET(user_resource.resources, ';')
ORDER BY
user_resource.user, isequence.id
'सामान्यीकृत' तालिका आउटपुट:
user resources resources_count resources_index resources_value
---------- --------- --------------- --------------- -----------------
sampleuser 1;2;3 3 1 1
sampleuser 1;2;3 3 2 2
sampleuser 1;2;3 3 3 3
stacky 2 1 1 2
testuser 1;3 2 1 1
testuser 1;3 2 2 3
उपरोक्त 'सामान्यीकृत' का उपयोग करना user_resources
तालिका, यह आवश्यक आउटपुट प्रदान करने के लिए एक सरल जुड़ाव है:
आवश्यक कार्य (ये सामान्य कार्य हैं जिनका उपयोग कहीं भी किया जा सकता है )
नोट:इन कार्यों के नाम mysql FIND_IN_SET फ़ंक्शन . यानी वे स्ट्रिंग सूचियों के संबंध में समान कार्य करते हैं?
COUNT_IN_SET
फ़ंक्शन:character delimited items
की गिनती लौटाता है कॉलम में।
DELIMITER $$
DROP FUNCTION IF EXISTS `COUNT_IN_SET`$$
CREATE FUNCTION `COUNT_IN_SET`(haystack VARCHAR(1024),
delim CHAR(1)
) RETURNS INTEGER
BEGIN
RETURN CHAR_LENGTH(haystack) - CHAR_LENGTH( REPLACE(haystack, delim, '')) + 1;
END$$
DELIMITER ;
VALUE_IN_SET
फ़ंक्शन:delimited list
का व्यवहार करता है एक one based array
. के रूप में और दिए गए 'इंडेक्स' पर मान लौटाता है।
DELIMITER $$
DROP FUNCTION IF EXISTS `VALUE_IN_SET`$$
CREATE FUNCTION `VALUE_IN_SET`(haystack VARCHAR(1024),
delim CHAR(1),
which INTEGER
) RETURNS VARCHAR(255) CHARSET utf8 COLLATE utf8_unicode_ci
BEGIN
RETURN SUBSTRING_INDEX(SUBSTRING_INDEX(haystack, delim, which),
delim,
-1);
END$$
DELIMITER ;
संबंधित जानकारी:
-
अंत में यह पता चला कि SQLFiddle - वर्किंग कोड कैसे प्राप्त करें कार्यों को संकलित करने के लिए।
-
इसका एक संस्करण है जो
SQLite
. के लिए काम करता है डेटाबेस भी SQLite- एक जुड़े हुए क्षेत्र को सामान्य बनाना और उसके साथ जुड़ना?
टेबल (डेटा के साथ):
CREATE TABLE `integerseries` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*Data for the table `integerseries` */
insert into `integerseries`(`id`) values (1);
insert into `integerseries`(`id`) values (2);
insert into `integerseries`(`id`) values (3);
insert into `integerseries`(`id`) values (4);
insert into `integerseries`(`id`) values (5);
insert into `integerseries`(`id`) values (6);
insert into `integerseries`(`id`) values (7);
insert into `integerseries`(`id`) values (8);
insert into `integerseries`(`id`) values (9);
insert into `integerseries`(`id`) values (10);
संसाधन:
CREATE TABLE `resource` (
`id` int(11) NOT NULL,
`data` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*Data for the table `resource` */
insert into `resource`(`id`,`data`) values (1,'abcde');
insert into `resource`(`id`,`data`) values (2,'qwerty');
insert into `resource`(`id`,`data`) values (3,'azerty');
User_resource:
CREATE TABLE `user_resource` (
`user` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`resources` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`user`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*Data for the table `user_resource` */
insert into `user_resource`(`user`,`resources`) values ('sampleuser','1;2;3');
insert into `user_resource`(`user`,`resources`) values ('stacky','3');
insert into `user_resource`(`user`,`resources`) values ('testuser','1;3');