What index(es) and queries for multiple column ranges search?Unexplained InnoDB timeoutsAre two indexes...

Identify a stage play about a VR experience in which participants are encouraged to simulate performing horrific activities

Science Fiction story where a man invents a machine that can help him watch history unfold

Would it be legal for a US State to ban exports of a natural resource?

Does "Dominei" mean something?

Word describing multiple paths to the same abstract outcome

How can a jailer prevent the Forge Cleric's Artisan's Blessing from being used?

Is there enough fresh water in the world to eradicate the drinking water crisis?

Resetting two CD4017 counters simultaneously, only one resets

Bob has never been a M before

I'm in charge of equipment buying but no one's ever happy with what I choose. How to fix this?

I2C signal and power over long range (10meter cable)

The One-Electron Universe postulate is true - what simple change can I make to change the whole universe?

Giant Toughroad SLR 2 for 200 miles in two days, will it make it?

Should my PhD thesis be submitted under my legal name?

How to check participants in at events?

Can you use Azure DevOps with Sitecore Managed Cloud

Greatest common substring

How to prevent YouTube from showing already watched videos?

The most efficient algorithm to find all possible integer pairs which sum to a given integer

Latex for-and in equation

Why is delta-v is the most useful quantity for planning space travel?

Simple image editor tool to draw a simple box/rectangle in an existing image

Could solar power be utilized and substitute coal in the 19th century?

Calculating the number of days between 2 dates in Excel



What index(es) and queries for multiple column ranges search?


Unexplained InnoDB timeoutsAre two indexes needed?Finding rows for a specified date rangeAdding index to large mysql tables#1093 - You can't specify target table 'R' for update in FROM clauseSimple query is slow on 4M-rows tableNeed help improving sql query performanceHow to improve query count execution with mySql replicate?Subquery and subselection for Count/Number doesnt workWhy does the Select statement stalls when executed directly in sequence?













0















Based on a simple innodb mysql 5.6 database schema like this:



test diagram



What is the optimal indexing strategy for querying the "info" table where most of the search criteria will be IN and BETWEEN conditions? Here are some constraints/info on the possible queries:




  • The date_created range will always be a part of the queries (BETWEEN);

  • The user_id and the info_category (JOIN) conditions are optional;

  • The results can be ordered by either the date_created or the view_count fields;


Here are some example queries to be executed:



#Fetch 100 active info based on date_created, user and info_type. 
#Ordered by date_created and offset 100000
SELECT i.id,
i.title,
i.view_count
FROM info i
WHERE i.date_created BETWEEN '2016-01-08 11:45:32' AND '2017-01-27 11:45:32'
AND i.info_type_id IN (1,2,3,23)
AND i.user_id IN (1,5,120,387,45023) #optional
AND i.id > 100000
AND i.date_deleted IS NULL
ORDER BY i.date_created DESC
LIMIT 100;


#Fetch 100 active info based on date_created, info_type.
#Ordered by view_count and offset 100000
SELECT i.id,
i.title,
i.view_count
FROM info i
WHERE i.date_created BETWEEN '2016-01-08 11:45:32' AND '2017-01-27 11:45:32'
AND i.info_type_id IN (1,2,3,23)
AND i.user_id IN (1,5,120,387,45023) #optional
AND i.id > 100000
AND i.date_deleted IS NULL
ORDER BY i.view_count DESC
LIMIT 100;


#Fetch 100 active info based on date_created, info_type, user and category.
#Ordered by view_count and offset 100000
SELECT i.id,
i.title,
i.view_count
FROM info i
JOIN info_category ic on ic.info_id = i.id AND ic.category_id IN (1,2,3,4,10)
WHERE i.date_created BETWEEN '2016-01-28 11:45:32' AND '2017-01-27 11:45:32'
AND i.info_type_id IN (1,2,3,23)
AND i.user_id IN (1,5,120,387,45023) #optional
AND i.id > 500000
AND i.date_deleted IS NULL
GROUP BY i.id
ORDER BY i.view_count DESC
LIMIT 100;


Here is the schema creation sql:



--
-- Schema test
--
DROP SCHEMA IF EXISTS `test` ;
CREATE SCHEMA IF NOT EXISTS `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
SHOW WARNINGS;

--
-- Set default database
--
USE test;

--
-- Definition for table info_type
--
DROP TABLE IF EXISTS info_type;
CREATE TABLE info_type (
id tinyint(1) UNSIGNED NOT NULL AUTO_INCREMENT,
name varchar(20) NOT NULL,
PRIMARY KEY (id)
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;

--
-- Definition for table category
--
DROP TABLE IF EXISTS category;
CREATE TABLE category (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
name varchar(20) NOT NULL,
PRIMARY KEY (id)
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci;

--
-- Definition for table user
--
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
name varchar(32) NOT NULL,
PRIMARY KEY (id)
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci;

--
-- Definition for table info
--
DROP TABLE IF EXISTS info;
CREATE TABLE info (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
title varchar(255) NOT NULL,
date_created datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
user_id int(10) UNSIGNED NOT NULL,
info_type_id tinyint(3) UNSIGNED NOT NULL,
date_deleted datetime DEFAULT NULL,
view_count int(11) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id),
INDEX date_created_idx (date_created),
INDEX fk_info_type_id_idx (info_type_id),
INDEX user_id_idx (user_id),
CONSTRAINT fk_info_type_id FOREIGN KEY (info_type_id)
REFERENCES info_type (id) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT fk_user_id FOREIGN KEY (user_id)
REFERENCES user (id) ON DELETE CASCADE ON UPDATE NO ACTION
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci;

--
-- Definition for table info_category
--
DROP TABLE IF EXISTS info_category;
CREATE TABLE info_category (
info_id INT(10) UNSIGNED NOT NULL,
category_id INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (info_id, category_id),
INDEX k_category_id_idx (category_id),
CONSTRAINT fk_info_id FOREIGN KEY (info_id)
REFERENCES info(id) ON DELETE CASCADE ON UPDATE NO ACTION,
CONSTRAINT fk_category_id FOREIGN KEY (category_id)
REFERENCES category(id) ON DELETE CASCADE ON UPDATE NO ACTION
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;


Here is a script to create dummy data (beware, took almost 3 hours on my crappy VM) :



INSERT INTO info_type(name)
VALUES ('news'), ('announcement'), ('advice'), ('memo'), ('historic');

DELIMITER //

DROP PROCEDURE IF EXISTS createFakeData//
CREATE PROCEDURE createFakeData ()
BEGIN

DECLARE userCount int default 50000;
DECLARE infoCount int default 1000000;
DECLARE categoryCount int DEFAULT 1000;
DECLARE infoCategoryCount int;
DECLARE i int DEFAULT 1;
DECLARE j int;

DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' BEGIN END;

#create fake users
WHILE i < userCount DO
INSERT INTO user(name) VALUES (CONCAT('user_', i));
SET i := i + 1;
END WHILE;

SET i = 1;

#create fake infos
WHILE i < infoCount DO
INSERT INTO info(title,
date_created,
user_id,
info_type_id,
view_count)
VALUES (CONCAT('title_', i),
DATE_ADD(NOW(), INTERVAL -(FLOOR(RAND() * 365) + 1) DAY),
FLOOR(RAND() * userCount) + 1,
FLOOR(RAND() * 5) + 1,
FLOOR(RAND() * 10000) + 1);

SET i := i + 1;
END WHILE;

SET i = 1;

#create fake categories
WHILE i < categoryCount DO
INSERT INTO category(name) VALUES (CONCAT('category_', i));
SET i = i + 1;
END WHILE;

SET i = 1;

#create fake info-category associations
WHILE i < infoCount DO
SET infoCategoryCount = FLOOR(RAND() * 10) + 1;
SET j = 0;

WHILE j < infoCategoryCount DO
INSERT INTO info_category (info_id, category_id)
VALUES (i, FLOOR(RAND() * categoryCount) + 1);
SET j = j + 1;
END WHILE;

SET i = i + 1;
END WHILE;
END//

DELIMITER ;

CALL createFakeData();
DROP PROCEDURE createFakeData;









share|improve this question














bumped to the homepage by Community 2 mins ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.




















    0















    Based on a simple innodb mysql 5.6 database schema like this:



    test diagram



    What is the optimal indexing strategy for querying the "info" table where most of the search criteria will be IN and BETWEEN conditions? Here are some constraints/info on the possible queries:




    • The date_created range will always be a part of the queries (BETWEEN);

    • The user_id and the info_category (JOIN) conditions are optional;

    • The results can be ordered by either the date_created or the view_count fields;


    Here are some example queries to be executed:



    #Fetch 100 active info based on date_created, user and info_type. 
    #Ordered by date_created and offset 100000
    SELECT i.id,
    i.title,
    i.view_count
    FROM info i
    WHERE i.date_created BETWEEN '2016-01-08 11:45:32' AND '2017-01-27 11:45:32'
    AND i.info_type_id IN (1,2,3,23)
    AND i.user_id IN (1,5,120,387,45023) #optional
    AND i.id > 100000
    AND i.date_deleted IS NULL
    ORDER BY i.date_created DESC
    LIMIT 100;


    #Fetch 100 active info based on date_created, info_type.
    #Ordered by view_count and offset 100000
    SELECT i.id,
    i.title,
    i.view_count
    FROM info i
    WHERE i.date_created BETWEEN '2016-01-08 11:45:32' AND '2017-01-27 11:45:32'
    AND i.info_type_id IN (1,2,3,23)
    AND i.user_id IN (1,5,120,387,45023) #optional
    AND i.id > 100000
    AND i.date_deleted IS NULL
    ORDER BY i.view_count DESC
    LIMIT 100;


    #Fetch 100 active info based on date_created, info_type, user and category.
    #Ordered by view_count and offset 100000
    SELECT i.id,
    i.title,
    i.view_count
    FROM info i
    JOIN info_category ic on ic.info_id = i.id AND ic.category_id IN (1,2,3,4,10)
    WHERE i.date_created BETWEEN '2016-01-28 11:45:32' AND '2017-01-27 11:45:32'
    AND i.info_type_id IN (1,2,3,23)
    AND i.user_id IN (1,5,120,387,45023) #optional
    AND i.id > 500000
    AND i.date_deleted IS NULL
    GROUP BY i.id
    ORDER BY i.view_count DESC
    LIMIT 100;


    Here is the schema creation sql:



    --
    -- Schema test
    --
    DROP SCHEMA IF EXISTS `test` ;
    CREATE SCHEMA IF NOT EXISTS `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
    SHOW WARNINGS;

    --
    -- Set default database
    --
    USE test;

    --
    -- Definition for table info_type
    --
    DROP TABLE IF EXISTS info_type;
    CREATE TABLE info_type (
    id tinyint(1) UNSIGNED NOT NULL AUTO_INCREMENT,
    name varchar(20) NOT NULL,
    PRIMARY KEY (id)
    )
    ENGINE = INNODB
    CHARACTER SET utf8
    COLLATE utf8_general_ci;

    --
    -- Definition for table category
    --
    DROP TABLE IF EXISTS category;
    CREATE TABLE category (
    id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    name varchar(20) NOT NULL,
    PRIMARY KEY (id)
    )
    ENGINE = INNODB
    AUTO_INCREMENT = 1
    CHARACTER SET utf8
    COLLATE utf8_general_ci;

    --
    -- Definition for table user
    --
    DROP TABLE IF EXISTS user;
    CREATE TABLE user (
    id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    name varchar(32) NOT NULL,
    PRIMARY KEY (id)
    )
    ENGINE = INNODB
    AUTO_INCREMENT = 1
    CHARACTER SET utf8
    COLLATE utf8_general_ci;

    --
    -- Definition for table info
    --
    DROP TABLE IF EXISTS info;
    CREATE TABLE info (
    id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    title varchar(255) NOT NULL,
    date_created datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
    user_id int(10) UNSIGNED NOT NULL,
    info_type_id tinyint(3) UNSIGNED NOT NULL,
    date_deleted datetime DEFAULT NULL,
    view_count int(11) UNSIGNED NOT NULL DEFAULT 0,
    PRIMARY KEY (id),
    INDEX date_created_idx (date_created),
    INDEX fk_info_type_id_idx (info_type_id),
    INDEX user_id_idx (user_id),
    CONSTRAINT fk_info_type_id FOREIGN KEY (info_type_id)
    REFERENCES info_type (id) ON DELETE RESTRICT ON UPDATE RESTRICT,
    CONSTRAINT fk_user_id FOREIGN KEY (user_id)
    REFERENCES user (id) ON DELETE CASCADE ON UPDATE NO ACTION
    )
    ENGINE = INNODB
    AUTO_INCREMENT = 1
    CHARACTER SET utf8
    COLLATE utf8_general_ci;

    --
    -- Definition for table info_category
    --
    DROP TABLE IF EXISTS info_category;
    CREATE TABLE info_category (
    info_id INT(10) UNSIGNED NOT NULL,
    category_id INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY (info_id, category_id),
    INDEX k_category_id_idx (category_id),
    CONSTRAINT fk_info_id FOREIGN KEY (info_id)
    REFERENCES info(id) ON DELETE CASCADE ON UPDATE NO ACTION,
    CONSTRAINT fk_category_id FOREIGN KEY (category_id)
    REFERENCES category(id) ON DELETE CASCADE ON UPDATE NO ACTION
    )
    ENGINE = INNODB
    CHARACTER SET utf8
    COLLATE utf8_general_ci;


    Here is a script to create dummy data (beware, took almost 3 hours on my crappy VM) :



    INSERT INTO info_type(name)
    VALUES ('news'), ('announcement'), ('advice'), ('memo'), ('historic');

    DELIMITER //

    DROP PROCEDURE IF EXISTS createFakeData//
    CREATE PROCEDURE createFakeData ()
    BEGIN

    DECLARE userCount int default 50000;
    DECLARE infoCount int default 1000000;
    DECLARE categoryCount int DEFAULT 1000;
    DECLARE infoCategoryCount int;
    DECLARE i int DEFAULT 1;
    DECLARE j int;

    DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' BEGIN END;

    #create fake users
    WHILE i < userCount DO
    INSERT INTO user(name) VALUES (CONCAT('user_', i));
    SET i := i + 1;
    END WHILE;

    SET i = 1;

    #create fake infos
    WHILE i < infoCount DO
    INSERT INTO info(title,
    date_created,
    user_id,
    info_type_id,
    view_count)
    VALUES (CONCAT('title_', i),
    DATE_ADD(NOW(), INTERVAL -(FLOOR(RAND() * 365) + 1) DAY),
    FLOOR(RAND() * userCount) + 1,
    FLOOR(RAND() * 5) + 1,
    FLOOR(RAND() * 10000) + 1);

    SET i := i + 1;
    END WHILE;

    SET i = 1;

    #create fake categories
    WHILE i < categoryCount DO
    INSERT INTO category(name) VALUES (CONCAT('category_', i));
    SET i = i + 1;
    END WHILE;

    SET i = 1;

    #create fake info-category associations
    WHILE i < infoCount DO
    SET infoCategoryCount = FLOOR(RAND() * 10) + 1;
    SET j = 0;

    WHILE j < infoCategoryCount DO
    INSERT INTO info_category (info_id, category_id)
    VALUES (i, FLOOR(RAND() * categoryCount) + 1);
    SET j = j + 1;
    END WHILE;

    SET i = i + 1;
    END WHILE;
    END//

    DELIMITER ;

    CALL createFakeData();
    DROP PROCEDURE createFakeData;









    share|improve this question














    bumped to the homepage by Community 2 mins ago


    This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.


















      0












      0








      0








      Based on a simple innodb mysql 5.6 database schema like this:



      test diagram



      What is the optimal indexing strategy for querying the "info" table where most of the search criteria will be IN and BETWEEN conditions? Here are some constraints/info on the possible queries:




      • The date_created range will always be a part of the queries (BETWEEN);

      • The user_id and the info_category (JOIN) conditions are optional;

      • The results can be ordered by either the date_created or the view_count fields;


      Here are some example queries to be executed:



      #Fetch 100 active info based on date_created, user and info_type. 
      #Ordered by date_created and offset 100000
      SELECT i.id,
      i.title,
      i.view_count
      FROM info i
      WHERE i.date_created BETWEEN '2016-01-08 11:45:32' AND '2017-01-27 11:45:32'
      AND i.info_type_id IN (1,2,3,23)
      AND i.user_id IN (1,5,120,387,45023) #optional
      AND i.id > 100000
      AND i.date_deleted IS NULL
      ORDER BY i.date_created DESC
      LIMIT 100;


      #Fetch 100 active info based on date_created, info_type.
      #Ordered by view_count and offset 100000
      SELECT i.id,
      i.title,
      i.view_count
      FROM info i
      WHERE i.date_created BETWEEN '2016-01-08 11:45:32' AND '2017-01-27 11:45:32'
      AND i.info_type_id IN (1,2,3,23)
      AND i.user_id IN (1,5,120,387,45023) #optional
      AND i.id > 100000
      AND i.date_deleted IS NULL
      ORDER BY i.view_count DESC
      LIMIT 100;


      #Fetch 100 active info based on date_created, info_type, user and category.
      #Ordered by view_count and offset 100000
      SELECT i.id,
      i.title,
      i.view_count
      FROM info i
      JOIN info_category ic on ic.info_id = i.id AND ic.category_id IN (1,2,3,4,10)
      WHERE i.date_created BETWEEN '2016-01-28 11:45:32' AND '2017-01-27 11:45:32'
      AND i.info_type_id IN (1,2,3,23)
      AND i.user_id IN (1,5,120,387,45023) #optional
      AND i.id > 500000
      AND i.date_deleted IS NULL
      GROUP BY i.id
      ORDER BY i.view_count DESC
      LIMIT 100;


      Here is the schema creation sql:



      --
      -- Schema test
      --
      DROP SCHEMA IF EXISTS `test` ;
      CREATE SCHEMA IF NOT EXISTS `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
      SHOW WARNINGS;

      --
      -- Set default database
      --
      USE test;

      --
      -- Definition for table info_type
      --
      DROP TABLE IF EXISTS info_type;
      CREATE TABLE info_type (
      id tinyint(1) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(20) NOT NULL,
      PRIMARY KEY (id)
      )
      ENGINE = INNODB
      CHARACTER SET utf8
      COLLATE utf8_general_ci;

      --
      -- Definition for table category
      --
      DROP TABLE IF EXISTS category;
      CREATE TABLE category (
      id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(20) NOT NULL,
      PRIMARY KEY (id)
      )
      ENGINE = INNODB
      AUTO_INCREMENT = 1
      CHARACTER SET utf8
      COLLATE utf8_general_ci;

      --
      -- Definition for table user
      --
      DROP TABLE IF EXISTS user;
      CREATE TABLE user (
      id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(32) NOT NULL,
      PRIMARY KEY (id)
      )
      ENGINE = INNODB
      AUTO_INCREMENT = 1
      CHARACTER SET utf8
      COLLATE utf8_general_ci;

      --
      -- Definition for table info
      --
      DROP TABLE IF EXISTS info;
      CREATE TABLE info (
      id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      title varchar(255) NOT NULL,
      date_created datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      user_id int(10) UNSIGNED NOT NULL,
      info_type_id tinyint(3) UNSIGNED NOT NULL,
      date_deleted datetime DEFAULT NULL,
      view_count int(11) UNSIGNED NOT NULL DEFAULT 0,
      PRIMARY KEY (id),
      INDEX date_created_idx (date_created),
      INDEX fk_info_type_id_idx (info_type_id),
      INDEX user_id_idx (user_id),
      CONSTRAINT fk_info_type_id FOREIGN KEY (info_type_id)
      REFERENCES info_type (id) ON DELETE RESTRICT ON UPDATE RESTRICT,
      CONSTRAINT fk_user_id FOREIGN KEY (user_id)
      REFERENCES user (id) ON DELETE CASCADE ON UPDATE NO ACTION
      )
      ENGINE = INNODB
      AUTO_INCREMENT = 1
      CHARACTER SET utf8
      COLLATE utf8_general_ci;

      --
      -- Definition for table info_category
      --
      DROP TABLE IF EXISTS info_category;
      CREATE TABLE info_category (
      info_id INT(10) UNSIGNED NOT NULL,
      category_id INT(10) UNSIGNED NOT NULL,
      PRIMARY KEY (info_id, category_id),
      INDEX k_category_id_idx (category_id),
      CONSTRAINT fk_info_id FOREIGN KEY (info_id)
      REFERENCES info(id) ON DELETE CASCADE ON UPDATE NO ACTION,
      CONSTRAINT fk_category_id FOREIGN KEY (category_id)
      REFERENCES category(id) ON DELETE CASCADE ON UPDATE NO ACTION
      )
      ENGINE = INNODB
      CHARACTER SET utf8
      COLLATE utf8_general_ci;


      Here is a script to create dummy data (beware, took almost 3 hours on my crappy VM) :



      INSERT INTO info_type(name)
      VALUES ('news'), ('announcement'), ('advice'), ('memo'), ('historic');

      DELIMITER //

      DROP PROCEDURE IF EXISTS createFakeData//
      CREATE PROCEDURE createFakeData ()
      BEGIN

      DECLARE userCount int default 50000;
      DECLARE infoCount int default 1000000;
      DECLARE categoryCount int DEFAULT 1000;
      DECLARE infoCategoryCount int;
      DECLARE i int DEFAULT 1;
      DECLARE j int;

      DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' BEGIN END;

      #create fake users
      WHILE i < userCount DO
      INSERT INTO user(name) VALUES (CONCAT('user_', i));
      SET i := i + 1;
      END WHILE;

      SET i = 1;

      #create fake infos
      WHILE i < infoCount DO
      INSERT INTO info(title,
      date_created,
      user_id,
      info_type_id,
      view_count)
      VALUES (CONCAT('title_', i),
      DATE_ADD(NOW(), INTERVAL -(FLOOR(RAND() * 365) + 1) DAY),
      FLOOR(RAND() * userCount) + 1,
      FLOOR(RAND() * 5) + 1,
      FLOOR(RAND() * 10000) + 1);

      SET i := i + 1;
      END WHILE;

      SET i = 1;

      #create fake categories
      WHILE i < categoryCount DO
      INSERT INTO category(name) VALUES (CONCAT('category_', i));
      SET i = i + 1;
      END WHILE;

      SET i = 1;

      #create fake info-category associations
      WHILE i < infoCount DO
      SET infoCategoryCount = FLOOR(RAND() * 10) + 1;
      SET j = 0;

      WHILE j < infoCategoryCount DO
      INSERT INTO info_category (info_id, category_id)
      VALUES (i, FLOOR(RAND() * categoryCount) + 1);
      SET j = j + 1;
      END WHILE;

      SET i = i + 1;
      END WHILE;
      END//

      DELIMITER ;

      CALL createFakeData();
      DROP PROCEDURE createFakeData;









      share|improve this question














      Based on a simple innodb mysql 5.6 database schema like this:



      test diagram



      What is the optimal indexing strategy for querying the "info" table where most of the search criteria will be IN and BETWEEN conditions? Here are some constraints/info on the possible queries:




      • The date_created range will always be a part of the queries (BETWEEN);

      • The user_id and the info_category (JOIN) conditions are optional;

      • The results can be ordered by either the date_created or the view_count fields;


      Here are some example queries to be executed:



      #Fetch 100 active info based on date_created, user and info_type. 
      #Ordered by date_created and offset 100000
      SELECT i.id,
      i.title,
      i.view_count
      FROM info i
      WHERE i.date_created BETWEEN '2016-01-08 11:45:32' AND '2017-01-27 11:45:32'
      AND i.info_type_id IN (1,2,3,23)
      AND i.user_id IN (1,5,120,387,45023) #optional
      AND i.id > 100000
      AND i.date_deleted IS NULL
      ORDER BY i.date_created DESC
      LIMIT 100;


      #Fetch 100 active info based on date_created, info_type.
      #Ordered by view_count and offset 100000
      SELECT i.id,
      i.title,
      i.view_count
      FROM info i
      WHERE i.date_created BETWEEN '2016-01-08 11:45:32' AND '2017-01-27 11:45:32'
      AND i.info_type_id IN (1,2,3,23)
      AND i.user_id IN (1,5,120,387,45023) #optional
      AND i.id > 100000
      AND i.date_deleted IS NULL
      ORDER BY i.view_count DESC
      LIMIT 100;


      #Fetch 100 active info based on date_created, info_type, user and category.
      #Ordered by view_count and offset 100000
      SELECT i.id,
      i.title,
      i.view_count
      FROM info i
      JOIN info_category ic on ic.info_id = i.id AND ic.category_id IN (1,2,3,4,10)
      WHERE i.date_created BETWEEN '2016-01-28 11:45:32' AND '2017-01-27 11:45:32'
      AND i.info_type_id IN (1,2,3,23)
      AND i.user_id IN (1,5,120,387,45023) #optional
      AND i.id > 500000
      AND i.date_deleted IS NULL
      GROUP BY i.id
      ORDER BY i.view_count DESC
      LIMIT 100;


      Here is the schema creation sql:



      --
      -- Schema test
      --
      DROP SCHEMA IF EXISTS `test` ;
      CREATE SCHEMA IF NOT EXISTS `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
      SHOW WARNINGS;

      --
      -- Set default database
      --
      USE test;

      --
      -- Definition for table info_type
      --
      DROP TABLE IF EXISTS info_type;
      CREATE TABLE info_type (
      id tinyint(1) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(20) NOT NULL,
      PRIMARY KEY (id)
      )
      ENGINE = INNODB
      CHARACTER SET utf8
      COLLATE utf8_general_ci;

      --
      -- Definition for table category
      --
      DROP TABLE IF EXISTS category;
      CREATE TABLE category (
      id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(20) NOT NULL,
      PRIMARY KEY (id)
      )
      ENGINE = INNODB
      AUTO_INCREMENT = 1
      CHARACTER SET utf8
      COLLATE utf8_general_ci;

      --
      -- Definition for table user
      --
      DROP TABLE IF EXISTS user;
      CREATE TABLE user (
      id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(32) NOT NULL,
      PRIMARY KEY (id)
      )
      ENGINE = INNODB
      AUTO_INCREMENT = 1
      CHARACTER SET utf8
      COLLATE utf8_general_ci;

      --
      -- Definition for table info
      --
      DROP TABLE IF EXISTS info;
      CREATE TABLE info (
      id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      title varchar(255) NOT NULL,
      date_created datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      user_id int(10) UNSIGNED NOT NULL,
      info_type_id tinyint(3) UNSIGNED NOT NULL,
      date_deleted datetime DEFAULT NULL,
      view_count int(11) UNSIGNED NOT NULL DEFAULT 0,
      PRIMARY KEY (id),
      INDEX date_created_idx (date_created),
      INDEX fk_info_type_id_idx (info_type_id),
      INDEX user_id_idx (user_id),
      CONSTRAINT fk_info_type_id FOREIGN KEY (info_type_id)
      REFERENCES info_type (id) ON DELETE RESTRICT ON UPDATE RESTRICT,
      CONSTRAINT fk_user_id FOREIGN KEY (user_id)
      REFERENCES user (id) ON DELETE CASCADE ON UPDATE NO ACTION
      )
      ENGINE = INNODB
      AUTO_INCREMENT = 1
      CHARACTER SET utf8
      COLLATE utf8_general_ci;

      --
      -- Definition for table info_category
      --
      DROP TABLE IF EXISTS info_category;
      CREATE TABLE info_category (
      info_id INT(10) UNSIGNED NOT NULL,
      category_id INT(10) UNSIGNED NOT NULL,
      PRIMARY KEY (info_id, category_id),
      INDEX k_category_id_idx (category_id),
      CONSTRAINT fk_info_id FOREIGN KEY (info_id)
      REFERENCES info(id) ON DELETE CASCADE ON UPDATE NO ACTION,
      CONSTRAINT fk_category_id FOREIGN KEY (category_id)
      REFERENCES category(id) ON DELETE CASCADE ON UPDATE NO ACTION
      )
      ENGINE = INNODB
      CHARACTER SET utf8
      COLLATE utf8_general_ci;


      Here is a script to create dummy data (beware, took almost 3 hours on my crappy VM) :



      INSERT INTO info_type(name)
      VALUES ('news'), ('announcement'), ('advice'), ('memo'), ('historic');

      DELIMITER //

      DROP PROCEDURE IF EXISTS createFakeData//
      CREATE PROCEDURE createFakeData ()
      BEGIN

      DECLARE userCount int default 50000;
      DECLARE infoCount int default 1000000;
      DECLARE categoryCount int DEFAULT 1000;
      DECLARE infoCategoryCount int;
      DECLARE i int DEFAULT 1;
      DECLARE j int;

      DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' BEGIN END;

      #create fake users
      WHILE i < userCount DO
      INSERT INTO user(name) VALUES (CONCAT('user_', i));
      SET i := i + 1;
      END WHILE;

      SET i = 1;

      #create fake infos
      WHILE i < infoCount DO
      INSERT INTO info(title,
      date_created,
      user_id,
      info_type_id,
      view_count)
      VALUES (CONCAT('title_', i),
      DATE_ADD(NOW(), INTERVAL -(FLOOR(RAND() * 365) + 1) DAY),
      FLOOR(RAND() * userCount) + 1,
      FLOOR(RAND() * 5) + 1,
      FLOOR(RAND() * 10000) + 1);

      SET i := i + 1;
      END WHILE;

      SET i = 1;

      #create fake categories
      WHILE i < categoryCount DO
      INSERT INTO category(name) VALUES (CONCAT('category_', i));
      SET i = i + 1;
      END WHILE;

      SET i = 1;

      #create fake info-category associations
      WHILE i < infoCount DO
      SET infoCategoryCount = FLOOR(RAND() * 10) + 1;
      SET j = 0;

      WHILE j < infoCategoryCount DO
      INSERT INTO info_category (info_id, category_id)
      VALUES (i, FLOOR(RAND() * categoryCount) + 1);
      SET j = j + 1;
      END WHILE;

      SET i = i + 1;
      END WHILE;
      END//

      DELIMITER ;

      CALL createFakeData();
      DROP PROCEDURE createFakeData;






      mysql index mysql-5.6 index-tuning






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Jan 30 '17 at 2:38









      chgachga

      1




      1





      bumped to the homepage by Community 2 mins ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.







      bumped to the homepage by Community 2 mins ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
























          1 Answer
          1






          active

          oldest

          votes


















          0














          It is essentially impossible to index on two ranges.



          Your comment about "offset ... order by ..." does not make sense because the offset is based on one column, the order by, another.



          The first query is best done with simply



          INDEX(date_created)


          which handles one of the ranges, plus the ORDER BY, thereby doing some filtering and avoiding a sort while being able to stop before the end of the table (if 100 rows are found).



          The other two are not that clear. Either have an index on the ORDER BY column so that sorting and limiting are handled, and/or have an index on whichever filtering column is the most selective.



          The JOIN in the third makes it even more difficult to optimize. The Optimizer can't know whether to start with ic or with i; both have filtering.






          share|improve this answer























            Your Answer








            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "182"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f162598%2fwhat-indexes-and-queries-for-multiple-column-ranges-search%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            It is essentially impossible to index on two ranges.



            Your comment about "offset ... order by ..." does not make sense because the offset is based on one column, the order by, another.



            The first query is best done with simply



            INDEX(date_created)


            which handles one of the ranges, plus the ORDER BY, thereby doing some filtering and avoiding a sort while being able to stop before the end of the table (if 100 rows are found).



            The other two are not that clear. Either have an index on the ORDER BY column so that sorting and limiting are handled, and/or have an index on whichever filtering column is the most selective.



            The JOIN in the third makes it even more difficult to optimize. The Optimizer can't know whether to start with ic or with i; both have filtering.






            share|improve this answer




























              0














              It is essentially impossible to index on two ranges.



              Your comment about "offset ... order by ..." does not make sense because the offset is based on one column, the order by, another.



              The first query is best done with simply



              INDEX(date_created)


              which handles one of the ranges, plus the ORDER BY, thereby doing some filtering and avoiding a sort while being able to stop before the end of the table (if 100 rows are found).



              The other two are not that clear. Either have an index on the ORDER BY column so that sorting and limiting are handled, and/or have an index on whichever filtering column is the most selective.



              The JOIN in the third makes it even more difficult to optimize. The Optimizer can't know whether to start with ic or with i; both have filtering.






              share|improve this answer


























                0












                0








                0







                It is essentially impossible to index on two ranges.



                Your comment about "offset ... order by ..." does not make sense because the offset is based on one column, the order by, another.



                The first query is best done with simply



                INDEX(date_created)


                which handles one of the ranges, plus the ORDER BY, thereby doing some filtering and avoiding a sort while being able to stop before the end of the table (if 100 rows are found).



                The other two are not that clear. Either have an index on the ORDER BY column so that sorting and limiting are handled, and/or have an index on whichever filtering column is the most selective.



                The JOIN in the third makes it even more difficult to optimize. The Optimizer can't know whether to start with ic or with i; both have filtering.






                share|improve this answer













                It is essentially impossible to index on two ranges.



                Your comment about "offset ... order by ..." does not make sense because the offset is based on one column, the order by, another.



                The first query is best done with simply



                INDEX(date_created)


                which handles one of the ranges, plus the ORDER BY, thereby doing some filtering and avoiding a sort while being able to stop before the end of the table (if 100 rows are found).



                The other two are not that clear. Either have an index on the ORDER BY column so that sorting and limiting are handled, and/or have an index on whichever filtering column is the most selective.



                The JOIN in the third makes it even more difficult to optimize. The Optimizer can't know whether to start with ic or with i; both have filtering.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Jan 30 '17 at 3:28









                Rick JamesRick James

                43.7k22259




                43.7k22259






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Database Administrators Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f162598%2fwhat-indexes-and-queries-for-multiple-column-ranges-search%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    ORA-01691 (unable to extend lob segment) even though my tablespace has AUTOEXTEND onORA-01692: unable to...

                    Always On Availability groups resolving state after failover - Remote harden of transaction...

                    Circunscripción electoral de Guipúzcoa Referencias Menú de navegaciónLas claves del sistema electoral en...