Is cross-table indexing possible?

前端 未结 3 595
遇见更好的自我
遇见更好的自我 2020-12-14 02:10

Consider a structure where you have a many-to-one (or one-to-many) relationship with a condition (where, order by, etc.) on both tables. For example:

CREATE         


        
相关标签:
3条回答
  • 2020-12-14 02:12

    As you know, SQLServer achieves this with indexed views:

    indexed views provide additional performance benefits that cannot be achieved using standard indexes. Indexed views can increase query performance in the following ways:

    Aggregations can be precomputed and stored in the index to minimize expensive computations during query execution.

    Tables can be prejoined and the resulting data set stored.

    Combinations of joins or aggregations can be stored.

    In SQLServer, to take advantage of this technique, you must query over the view and not over the tables. That means that you should know about the view and indexes.

    MySQL does not have indexed views, but you can simulate the behavior with table + triggers + indexes.

    Instead of creating a view, you must create an indexed table, a trigger to keep the data table up to date, and then you must query your new table instead of your normalized tables.

    You must evaluate if the overhead of write operations offsets the improvement in read operations.

    Edited:

    Note that it is not always necessary to create a new table. For example, in a 1:N relationship (master-detail) trigger, you can keep a copy of a field from the 'master' table into the 'detail' table. In your case:

    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;
    

    Notice that this is a before insert trigger.

    Now, the query is rewritten as follows:

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

    Disclaimer: not tested.

    0 讨论(0)
  • 2020-12-14 02:15

    Cross-table indexing is not possible in MySQL except via the now-defunct Akiban(?) Engine.

    I have a rule: "Do not normalize 'continuous' values such as INTs, FLOATs, DATETIMEs, etc." The cost of the JOIN when you need to sort or range-test on the continuous value will kill performance.

    DATETIME takes 5 bytes; INT takes 4. So any 'space' argument toward normalizing a datetime is rather poor. It is rare that you would need to 'normalize' a datetime in the off chance that all uses of a particular value were to change.

    0 讨论(0)
  • 2020-12-14 02:30

    May be I'm wrong , but if this is my application I will not duplicate the data unless I need to order by 2 columns in 2 different tables and this is a hot query (it's required many times). But since there is no clear cut solution to avoid the filesort, what about this little trick (force the optimizer to use the index on the column in the order by clause eventTime)

    select * from tableOne t1 
    inner join tableTwo t2 use index (eventTime)  on t1.tableTwoId = t2.id and t2.eventTime > 0
    where t1.objectId = 1
    order by t2.eventTime desc limit 0,10;
    

    notice use index (eventTime) and t2.eventTime > 0

    It's explain shows that the optimizer has used the index on eventTime instead of filesort

    1   SIMPLE  t2  range   eventTime   eventTime   5       5000    Using where; Using index
    1   SIMPLE  t1  ref objectId,tableTwoId tableTwoId  4   tests.t2.id 1   Using where
    
    0 讨论(0)
提交回复
热议问题