2011-12-14 4 views
14

ऐसी संरचना पर विचार करें जहां आपके पास दोनों टेबलों पर एक शर्त (जहां, ऑर्डर इत्यादि) के साथ कई से एक (या एक से कई) संबंध हैं। उदाहरण के लिए:क्या क्रॉस-टेबल इंडेक्सिंग संभव है?

CREATE TABLE tableTwo (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, 
    eventTime DATETIME NOT NULL, 
    INDEX (eventTime) 
) ENGINE=InnoDB; 

CREATE TABLE tableOne (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, 
    tableTwoId INT UNSIGNED NOT NULL, 
    objectId INT UNSIGNED NOT NULL, 
    INDEX (objectID), 
    FOREIGN KEY (tableTwoId) REFERENCES tableTwo (id) 
) ENGINE=InnoDB; 

और एक उदाहरण क्वेरी के लिए:

select * from tableOne t1 
    inner join tableTwo t2 on t1.tableTwoId = t2.id 
    where objectId = '..' 
    order by eventTime; 

मान लें कि आपको सूचकांक tableOne.objectId और tableTwo.eventTime करते हैं। यदि आप उपरोक्त क्वेरी पर व्याख्या करते हैं, तो यह "फाइलों का उपयोग करना" दिखाएगा। अनिवार्य रूप से, यह पहले tableOne.objectId अनुक्रमणिका लागू करता है, लेकिन यह tableTwo.eventTime अनुक्रमणिका लागू नहीं कर सकता है क्योंकि यह सूचकांक तालिका की पूरी तरह से है (सीमित परिणाम सेट नहीं), और इस प्रकार इसे मैन्युअल प्रकार करना चाहिए।

इस प्रकार, एक क्रॉस-टेबल इंडेक्स करने का कोई तरीका है, इसलिए प्रत्येक बार परिणाम पुनर्प्राप्त करने के लिए उसे फाइलोर्ट नहीं करना पड़ेगा? कुछ की तरह:

create index ind_t1oi_t2et on tableOne t1 
    inner join tableTwo t2 on t1.tableTwoId = t2.id 
    (t1.objectId, t2.eventTime); 

इसके अलावा, मैं एक दृश्य और अनुक्रमण कि बनाने में देखा है, लेकिन अनुक्रमण विचारों के लिए समर्थित नहीं है।

क्रॉस-टेबल इंडेक्सिंग संभव नहीं है तो समाधान मैं एक टेबल में सशर्त डेटा को दोहराना संभव नहीं कर रहा हूं। इस मामले में इसका अर्थ है eventTimetableOne में दोहराया जाएगा और एक बहु-कॉलम इंडेक्स tableOne.objectId और tableOne.eventTime (अनिवार्य रूप से मैन्युअल रूप से इंडेक्स बनाने) पर स्थापित किया जाएगा। हालांकि, मैंने सोचा कि मैं यह देखने के लिए कि क्या यह सबसे अच्छा तरीका था, मैं पहले लोगों के अनुभव की तलाश करूँगा।

बहुत बहुत धन्यवाद!

अपडेट: (

drop procedure if exists populate_table_two; 
delimiter # 
create procedure populate_table_two(IN numRows int) 
begin 
declare v_counter int unsigned default 0; 
    while v_counter < numRows do 
    insert into tableTwo (eventTime) 
    values (CURRENT_TIMESTAMP - interval 0 + floor(0 + rand()*1000) minute); 
    set v_counter=v_counter+1; 
    end while; 
end # 
delimiter ; 

drop procedure if exists populate_table_one; 
delimiter # 
create procedure populate_table_one 
    (IN numRows int, IN maxTableTwoId int, IN maxObjectId int) 
begin 
declare v_counter int unsigned default 0; 
    while v_counter < numRows do 
    insert into tableOne (tableTwoId, objectId) 
     values (floor(1 +(rand() * maxTableTwoId)), 
       floor(1 +(rand() * maxObjectId))); 
    set v_counter=v_counter+1; 
    end while; 
end # 
delimiter ; 

रूप tableOne में tableTwo में 10,000 पंक्तियों और 20,000 पंक्तियों को भरने के लिए इस प्रकार आप इन का उपयोग कर सकते के साथ:

यहाँ लोड हो रहा है परीक्षण डाटा के लिए कुछ प्रक्रियाओं और की तुलना परिणाम हैं tableOne और यादृच्छिक objectId एस 1 और 5 के बीच यादृच्छिक संदर्भ), जो मेरे लिए क्रमशः 26.2 और 70.77 सेकेंड लेते हैं:

call populate_table_two(10000); 
call populate_table_one(20000, 10000, 5); 

अद्यतन 2 (परीक्षण ट्रिगर एसक्यूएल):

नीचे daniHp के ट्रिगर विधि के आधार पर करने की कोशिश की और परीक्षण किया एसक्यूएल है। यह tableOne पर सिंक में dateTime रखता है जब tableOne जोड़ा जाता है या tableTwo अपडेट किया जाता है। साथ ही, इस विधि को कई से अधिक रिश्तों के लिए भी काम करना चाहिए यदि स्थिति कॉलम को शामिल करने वाली तालिका में कॉपी किया गया हो। tableOne में 300,000 पंक्तियों के परीक्षण और tableTwo में 200,000 पंक्तियां, समान सीमा वाले पुरानी क्वेरी की गति 0.12 सेकंड थी और नई क्वेरी की गति अभी भी 0.00 सेकंड के रूप में दिखाई देती है। इस प्रकार, एक स्पष्ट सुधार है, और इस विधि को लाखों पंक्तियों और आगे में अच्छी तरह से प्रदर्शन करना चाहिए।

alter table tableOne add column tableTwo_eventTime datetime; 

create index ind_t1_oid_t2et on tableOne (objectId, tableTwo_eventTime); 

drop TRIGGER if exists t1_copy_t2_eventTime; 
delimiter # 
CREATE TRIGGER t1_copy_t2_eventTime 
    BEFORE INSERT ON tableOne 
for each row 
begin 
    set NEW.tableTwo_eventTime = (select eventTime 
     from tableTwo t2 
     where t2.id = NEW.tableTwoId); 
end # 
delimiter ; 

drop TRIGGER if exists upd_t1_copy_t2_eventTime; 
delimiter # 
CREATE TRIGGER upd_t1_copy_t2_eventTime 
    BEFORE UPDATE ON tableTwo 
for each row 
begin 
    update tableOne 
    set tableTwo_eventTime = NEW.eventTime 
    where tableTwoId = NEW.id; 
end # 
delimiter ; 

और अद्यतन क्वेरी:

select * from tableOne t1 
    inner join tableTwo t2 on t1.tableTwoId = t2.id 
    where t1.objectId = 1 
    order by t1.tableTwo_eventTime desc limit 0,10; 
+1

आप किसी अन्य के लिए एकत्रित तालिका बना सकते हैं। – anttir

+0

@anttir: क्या ऐसा कोई कारण है जो मौजूदा तालिकाओं में से किसी एक में डेटा को दोहराने के लिए बेहतर होगा? – Briguy37

+0

[नमूना कोड] (http://sscce.org/) (यहां, एसक्यूएल के रूप में) विज्ञापन स्कीमा से अधिक उपयोगी है। – outis

उत्तर

5

कि आप जानते हैं, SQLServer indexed views के साथ इस को प्राप्त होता है:

अनुक्रमित विचारों अतिरिक्त प्रदर्शन लाभ है कि नहीं किया जा सकता मानक अनुक्रमित का प्रयोग कर प्राप्त प्रदान करते हैं। इंडेक्स्ड विचारों को निम्न तरीकों से क्वेरी प्रदर्शन को बढ़ाने कर सकते हैं:

एकत्रीकरण precomputed और सूचकांक में संग्रहीत क्वेरी निष्पादन के दौरान महंगा संगणना कम करने के लिए किया जा सकता है।

टेबल्स को पहले से जोड़ा जा सकता है और परिणामस्वरूप डेटा सेट किया जा सकता है।

जॉइन या समेकन के संयोजन संग्रहीत किए जा सकते हैं।

SQLServer में, इस तकनीक का लाभ उठाने के लिए, आपको दृश्यों पर क्वेरी करना चाहिए और तालिकाओं पर नहीं। इसका मतलब है कि आपको दृश्य और अनुक्रमणिका के बारे में पता होना चाहिए।

MySQL में अनुक्रमित विचार नहीं हैं, लेकिन आप तालिका + ट्रिगर्स + इंडेक्स के साथ व्यवहार अनुकरण कर सकते हैं।

एक दृश्य बनाने के बजाय, आपको एक अनुक्रमित तालिका, डेटा तालिका को अद्यतित रखने के लिए एक ट्रिगर बनाना होगा, और फिर आपको अपनी सामान्य तालिका के बजाय अपनी नई तालिका से पूछना होगा।

आपको मूल्यांकन करना होगा कि क्या लिखने के संचालन के ओवरहेड पढ़ने के संचालन में सुधार को समाप्त करता है।

संपादित:

ध्यान दें कि यह हमेशा एक नई तालिका बनाने के लिए आवश्यक है। उदाहरण के लिए, 1: एन रिलेशनशिप (मास्टर-विस्तार) ट्रिगर में, आप 'मास्टर' टेबल से 'फ़ील्ड' तालिका में किसी फ़ील्ड की प्रतिलिपि रख सकते हैं। आपके मामले में:

CREATE TABLE tableOne (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, 
    tableTwoId INT UNSIGNED NOT NULL, 
    objectId INT UNSIGNED NOT NULL, 
    desnormalized_eventTime DATETIME NOT NULL, 
    INDEX (objectID), 
    FOREIGN KEY (tableTwoId) REFERENCES tableTwo (id) 
) ENGINE=InnoDB; 

CREATE TRIGGER tableOne_desnormalized_eventTime 
    BEFORE INSERT ON tableOne 
for each row 
begin 
    DECLARE eventTime DATETIME; 
    SET eventTime = 
     (select eventTime 
     from tableOne 
     where tableOne.id = NEW.tableTwoId); 
    NEW.desnormalized_eventTime = eventTime; 
end; 

सूचना है कि इस डालने ट्रिगर से पहले एक है।

अब, क्वेरी इस प्रकार के रूप में लिखा जाता है:

select * from tableOne t1 
    inner join tableTwo t2 on t1.tableTwoId = t2.id 
    where t1.objectId = '..' 
    order by t1.desnormalized_eventTime; 

अस्वीकरण: परीक्षण नहीं किया।

+0

+1: मुझे इंडेक्सिंग डेटा की प्रतिलिपि बनाने के लिए ट्रिगर्स का उपयोग करने का विचार पसंद है! मैं संभवतः इस विधि के साथ जाऊंगा, लेकिन 'eventTime' को सीधे' tableOne' में जोड़ें, क्योंकि इसे कॉपी करने के लिए प्रतिलिपि, कोड-पुनर्लेखन और अतिरिक्त स्मृति खपत को कम करना चाहिए। – Briguy37

+0

अच्छा। यदि यह डेटाबेस है जो प्रतिलिपि डेटा बनाए रखता है, तो वे अपडेट करने के लिए भूलने का कोई तरीका नहीं हैं। इस समय, मैं ओआरएम (डीजेंगो) के साथ काम कर रहा हूं और मैं इस तरह का कोड सहेजता हूं() विधि (ऑब्जेक्ट दृढ़ता)।मुझे सामान्यीकरण कारणों से ऐसा करने में हिचकिचाहट हुई, लेकिन मुझे डेटा दोहराने में खुशी हुई। 'अकादमिक डेवलपर' के लिए एक कठिन निर्णय है;) – danihp

+0

नोट: इस समाधान का उपयोग करने वाले लोगों के लिए, 'eventTime' पर एक अपडेट ट्रिगर भी जोड़ना सुनिश्चित करें, साथ ही' eventTime' निश्चित नहीं है। – Briguy37

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