2015-07-28 4 views
15

मेरे पास निम्न प्रश्न है। विचार यह है कि यह मुझे यह जानने की अनुमति देता है कि groups, और बाद में users, प्रत्येक component_instance तक पहुंच है। अगर वहाँ प्रश्न के रूप में यह करने के लिए एक बेहतर तरीका है काफी धीमी है मैं सोच रहा हूँ, लेकिन यह इन अतिरिक्त कॉलम हर बार जब मैं इस तालिका से निपटने के लिए वास्तव में आसान है:एक GROUP_CONCAT क्वेरी को और अधिक कुशल बनाना

SELECT component_instances.*, 
GROUP_CONCAT(DISTINCT IF(permissions.view, groups.id, NULL)) AS view_group_ids, 
GROUP_CONCAT(DISTINCT IF(permissions.edit, groups.id, NULL)) AS edit_group_ids, 
GROUP_CONCAT(DISTINCT IF(permissions.view, users.id, NULL)) AS view_user_ids, 
GROUP_CONCAT(DISTINCT IF(permissions.edit, users.id, NULL)) AS edit_user_ids 
FROM `component_instances` 
LEFT OUTER JOIN permissions ON permissions.component_instance_id = component_instances.id 
LEFT OUTER JOIN groups ON groups.id = permissions.group_id 
LEFT OUTER JOIN groups_users ON groups_users.group_id = groups.id 
LEFT OUTER JOIN users ON users.id = groups_users.user_id 
GROUP BY component_instances.id 
ORDER BY (case when component_instances.ancestry is null then 0 else 1 end), component_instances.ancestry, position 

अनुमतियाँ तालिका इसलिए की तरह है (रेल बहाना!):

create_table "permissions", :force => true do |t| 
    t.integer "component_instance_id" 
    t.integer "group_id" 
    t.boolean "view",     :default => false 
    t.boolean "edit",     :default => false 
end 

अनुमतियों के प्रकार edit, और view हैं। एक समूह को या दोनों को सौंपा जा सकता है। यदि component_instance पर कोई समूह अनुमति नहीं है, तो अनुमतियां भी पुनरावर्ती हैं, हमें पहले अपने पूर्वजों को जांचना होगा जहां अनुमतियां सेट की गई हैं (यदि कोई हो)। इससे एक प्रश्न बहुत महत्वपूर्ण हो जाता है क्योंकि मैं इस क्वेरी को चयन तर्क के साथ जोड़ सकता हूं कि ancestry मणि (भौतिक पथ पथ) प्रदान करता है।

अद्यतन

मैंने जब से इस क्वेरी मानक तेजी से मिला:

SELECT component_instances.*, 
GROUP_CONCAT(DISTINCT view_groups.id) AS view_group_ids, 
GROUP_CONCAT(DISTINCT edit_groups.id) AS edit_group_ids, 
GROUP_CONCAT(DISTINCT view_users.id) AS view_user_ids, 
GROUP_CONCAT(DISTINCT edit_users.id) AS edit_user_ids 
FROM `component_instances` 
LEFT OUTER JOIN permissions ON permissions.component_instance_id = component_instances.id 
LEFT OUTER JOIN groups view_groups ON view_groups.id = permissions.group_id AND permissions.view = 1 
LEFT OUTER JOIN groups edit_groups ON edit_groups.id = permissions.group_id AND permissions.edit = 1 
LEFT OUTER JOIN groups_users view_groups_users ON view_groups_users.group_id = view_groups.id 
LEFT OUTER JOIN groups_users edit_groups_users ON edit_groups_users.group_id = edit_groups.id 
LEFT OUTER JOIN users view_users ON view_users.id = view_groups_users.user_id 
LEFT OUTER JOIN users edit_users ON edit_users.id = edit_groups_users.user_id 
GROUP BY component_instances.id 
ORDER BY (case when component_instances.ancestry is null then 0 else 1 end), component_instances.ancestry, position 

यहाँ एक ऊपर क्वेरी के लिए समझाने और तालिका बनाने के बयान है:

+----+-------------+---------------------+--------+-----------------------------------------------+--------------------------------------------+---------+--------------------------------------------+------+------------------------------------------------------+ 
| id | select_type | table    | type | possible_keys         | key          | key_len | ref          | rows | Extra            | 
+----+-------------+---------------------+--------+-----------------------------------------------+--------------------------------------------+---------+--------------------------------------------+------+------------------------------------------------------+ 
| 1 | SIMPLE  | component_instances | ALL | PRIMARY,index_component_instances_on_ancestry | NULL          | NULL | NULL          | 119 | "Using temporary; Using filesort"     | 
| 1 | SIMPLE  | permissions   | ALL | NULL           | NULL          | NULL | NULL          | 6 | "Using where; Using join buffer (Block Nested Loop)" | 
| 1 | SIMPLE  | view_groups   | eq_ref | PRIMARY          | PRIMARY         | 4  | 05707d890df9347c.permissions.group_id  | 1 | "Using where; Using index"       | 
| 1 | SIMPLE  | edit_groups   | eq_ref | PRIMARY          | PRIMARY         | 4  | 05707d890df9347c.permissions.group_id  | 1 | "Using where; Using index"       | 
| 1 | SIMPLE  | view_groups_users | ref | index_groups_users_on_group_id_and_user_id | index_groups_users_on_group_id_and_user_id | 5  | 05707d890df9347c.view_groups.id   | 1 | "Using index"          | 
| 1 | SIMPLE  | edit_groups_users | ref | index_groups_users_on_group_id_and_user_id | index_groups_users_on_group_id_and_user_id | 5  | 05707d890df9347c.edit_groups.id   | 1 | "Using index"          | 
| 1 | SIMPLE  | view_users   | eq_ref | PRIMARY          | PRIMARY         | 4  | 05707d890df9347c.view_groups_users.user_id | 1 | "Using index"          | 
| 1 | SIMPLE  | edit_users   | eq_ref | PRIMARY          | PRIMARY         | 4  | 05707d890df9347c.edit_groups_users.user_id | 1 | "Using index"          | 
+----+-------------+---------------------+--------+-----------------------------------------------+--------------------------------------------+---------+--------------------------------------------+------+------------------------------------------------------+ 

CREATE TABLE `component_instances` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `visible` int(11) DEFAULT '1', 
    `instance_id` int(11) DEFAULT NULL, 
    `deleted_on` date DEFAULT NULL, 
    `instance_type` varchar(255) DEFAULT NULL, 
    `component_id` int(11) DEFAULT NULL, 
    `deleted_root_item` int(11) DEFAULT NULL, 
    `locked_until` datetime DEFAULT NULL, 
    `theme_id` int(11) DEFAULT NULL, 
    `position` int(11) DEFAULT NULL, 
    `ancestry` varchar(255) DEFAULT NULL, 
    `ancestry_depth` int(11) DEFAULT '0', 
    `cached_name` varchar(255) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `index_component_instances_on_ancestry` (`ancestry`) 
) ENGINE=InnoDB AUTO_INCREMENT=121 DEFAULT CHARSET=utf8 

CREATE TABLE `groups` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) NOT NULL DEFAULT '', 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 

CREATE TABLE `groups_users` (
    `group_id` int(11) DEFAULT NULL, 
    `user_id` int(11) DEFAULT NULL, 
    KEY `index_groups_users_on_group_id_and_user_id` (`group_id`,`user_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

CREATE TABLE `permissions` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `component_instance_id` int(11) DEFAULT NULL, 
    `group_id` int(11) DEFAULT NULL, 
    `view` tinyint(1) DEFAULT '0', 
    `edit` tinyint(1) DEFAULT '0', 
    PRIMARY KEY (`id`), 
    KEY `edit_permissions_index` (`edit`,`group_id`,`component_instance_id`), 
    KEY `view_permissions_index` (`view`,`group_id`,`component_instance_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 

CREATE TABLE `users` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `real_name` varchar(255) DEFAULT NULL, 
    `username` varchar(255) NOT NULL DEFAULT '', 
    `email` varchar(255) NOT NULL DEFAULT '', 
    `crypted_password` varchar(255) DEFAULT NULL, 
    `administrator` int(11) NOT NULL DEFAULT '0', 
    `password_salt` varchar(255) DEFAULT NULL, 
    `remember_token_expires` datetime DEFAULT NULL, 
    `persistence_token` varchar(255) DEFAULT NULL, 
    `disabled` tinyint(1) DEFAULT NULL, 
    `time_zone` varchar(255) DEFAULT NULL, 
    `login_count` int(11) DEFAULT NULL, 
    `failed_login_count` int(11) DEFAULT NULL, 
    `last_request_at` datetime DEFAULT NULL, 
    `current_login_at` datetime DEFAULT NULL, 
    `last_login_at` datetime DEFAULT NULL, 
    `current_login_ip` varchar(255) DEFAULT NULL, 
    `last_login_ip` varchar(255) DEFAULT NULL, 
    `perishable_token` varchar(255) NOT NULL DEFAULT '', 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `index_users_on_username` (`username`), 
    KEY `index_users_on_perishable_token` (`perishable_token`) 
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 

ORDER BYancestry gem से आता है लेकिन यदि ऐसा करने का एक बेहतर तरीका है तो मुझे submi से प्रसन्नता होगी टी कि उन्हें एक पुल अनुरोध के रूप में।

+0

यह प्रश्न में अपने सभी पाठ रखने मैं एक 'UPDATE' लाइन का उपयोग मेरी प्रत्येक अद्यतन को अलग करने और सभी प्रश्न भाग में स्टेम रखने होगा अगर मैं तुम्हें थे प्रथागत है। यह पढ़ने के लिए और अधिक स्पष्ट बनाता है। – Mehran

+0

धन्यवाद मेहरान, मैंने इसे अपडेट किया है। मैं शुरू में अपने स्वयं के प्रश्न का उत्तर देने गया, फिर एक बक्षीस करने के लिए सोचा। –

+0

यह भी मुझे लगता है कि यदि आप दूसरे संस्करण का उपयोग कर रहे हैं तो आप पिछले दो जोड़ों को छोड़ सकते हैं और group_concat – maraca

उत्तर

1

NULL पहले रखा गया है (COALESCE का उपयोग NULL को अतिरिक्त सॉर्ट कॉलम का उपयोग करने के बजाय कुछ और करने के लिए भी कर सकता है)। दूसरी बात जॉइन को कम कर रही है, क्योंकि आखिरी दो आईडी पर थे जिस पर हम सहमत थे।

SELECT 
    component_instances.*, 
    GROUP_CONCAT(DISTINCT view_groups.id) AS view_group_ids, 
    GROUP_CONCAT(DISTINCT edit_groups.id) AS edit_group_ids, 
    GROUP_CONCAT(DISTINCT view_groups_users.user_id) AS view_user_ids, 
    GROUP_CONCAT(DISTINCT edit_groups_users.user_id) AS edit_user_ids 
FROM 
    `component_instances` 
    LEFT OUTER JOIN permissions 
     ON permissions.component_instance_id = component_instances.id 
    LEFT OUTER JOIN groups view_groups 
     ON view_groups.id = permissions.group_id AND permissions.view = 1 
    LEFT OUTER JOIN groups edit_groups 
     ON edit_groups.id = permissions.group_id AND permissions.edit = 1 
    LEFT OUTER JOIN groups_users view_groups_users 
     ON view_groups_users.group_id = view_groups.id 
    LEFT OUTER JOIN groups_users edit_groups_users 
     ON edit_groups_users.group_id = edit_groups.id 
GROUP BY 
    component_instances.id 
ORDER BY 
    component_instances.ancestry, -- MySQL was sorting the NULL values already correctly 
    position 
; 
+0

धन्यवाद माराका, क्षमा करें मैंने अन्य उपयोगकर्ताओं को पहले जवाब दिया क्योंकि मैंने सोचा था कि यह आप थे! मैंने इसे उलट दिया है। आप सही हैं, NULL पहले रखा गया है। मुझे संदेह है कि कोड का थोड़ा सा अन्य डेटाबेस प्रकार का समर्थन करने के लिए शायद पूर्वजों पुस्तकालय के रूप में सिर्फ MySQL के लिए नहीं है। हालांकि मैं उस हिस्से को ओवरराइड कर सकता हूं, इसलिए मैं ऐसा करूँगा। –

+0

दुर्भाग्य से उपरोक्त आपकी क्वेरी view_user_ids और edit_user_ids के लिए अलग-अलग परिणामों में परिणाम जोड़ती है। वे दोनों एक ही समय में निष्पादित होते हैं, इसलिए जब तक आप यह नहीं समझना चाहते कि ऐसा क्यों हो सकता है, तो मुझे उप-चयन के बिना सरल उत्तर स्वीकार करने में खुशी होगी। –

+0

मुझे लगता है कि वे होना चाहिए। परिणामों को देखते हुए, ऐसा लगता है कि उप-चयन केवल पहली समूह आईडी को पकड़ रहा है और बाकी को अनदेखा कर रहा है। अतिरिक्त जुड़ने निश्चित रूप से अपेक्षित के रूप में काम करते हैं। –

2

यदि आपकी टेबल संरचना और सूचकांक नहीं हैं तो आपकी क्वेरी को अनुकूलित करना लगभग असंभव है। EXPLAIN कथन का उपयोग क्वेरी अनुकूलन का आवश्यक हिस्सा है।

उल्लिखित जानकारी के बिना, मैं आपके प्रश्न पर टिप्पणी कर सकता हूं कि आपके ORDER BY भाग निश्चित रूप से कुछ अनुकूलन से लाभ उठा सकते हैं। किसी शर्त में किसी भी कार्य या बयान का उपयोग करने से हमेशा आपदा हो जाएगी। ORDER BY में एक शून्य क्षेत्र का उपयोग करने से भी समस्याएं पैदा हो जाएंगी। शायद सबसे आसान तरीका वर्तमान CASE कथन के बजाय 0 और 1s धारण करने वाली आपकी तालिका में एक नया फ़ील्ड जोड़ देगा।

यह न भूलें कि रिकॉर्ड की संख्या काफी महत्वपूर्ण है, तो किसी भी शर्त/आदेश के भीतर किसी भी क्षेत्र में इंडेक्स होने पर हमेशा आवश्यक है।

[अद्यतन]

आपकी क्वेरी बल्कि सरल है। EXPLAIN के परिणाम से पता चलता है कि केवल कुछ हिस्सों के लिए एक उम्मीदवार के रूप में उपयुक्त अनुक्रमित करने के लिए कर रहे हैं:

CREATE INDEX inx4 ON permissions (`component_instance_id`, `group_id`, `edit`, `view`); 

EXPLAIN की दूसरी पंक्ति से पता चलता तालिका permissions आपकी क्वेरी में उपयोग का कोई सूचकांक नहीं है कि। ऐसा इसलिए है क्योंकि MySQL में कुछ नियम हैं जब यह इंडेक्स का उपयोग करेगा:

  • प्रत्येक तालिका का केवल एक अनुक्रमणिका प्रत्येक (उप-) क्वेरी में उपयोग किया जा सकता है।
  • किसी भी इंडेक्स का उपयोग तभी किया जा सकता है जब उसके सभी फ़ील्ड क्वेरी में उल्लिखित हों (जैसे/शर्तों द्वारा/समूह में)।

आपकी क्वेरी, और तथ्य यह है कि टेबल permissions के सभी चार क्षेत्रों का उल्लेख कर रहे ध्यान में रखते हुए, आप उन सभी चार पर एक सूचकांक की आवश्यकता होगी या यह बेकार है।

फिर भी ORDER BY पहले उल्लेख किए गए संशोधन से लाभ उठा सकता है।

+0

धन्यवाद मेहरान, मैंने आपके द्वारा अनुरोधित अतिरिक्त विवरणों में जोड़ा है। मैं निश्चित रूप से ऑर्डर द्वारा कथन में रूचि रखता हूं, अद्यतन प्रश्न देखें। मैंने प्रश्न में से किसी एक के बजाय उपरोक्त मेरे उत्तर में क्वेरी का खुलासा किया है। –

संबंधित मुद्दे