큰 테이블에서 LEFT JOIN을 사용하여 매우 느린 SELECT를 최적화하는 방법


15

나는 인터넷 검색, 자체 교육 및 몇 시간 동안 해결책을 찾고 있었지만 운이 없었습니다. 나는 여기서 몇 가지 비슷한 질문을 찾았지만이 경우는 아닙니다.

내 테이블 :

  • 인원 (~ 10M 행)
  • 속성 (위치, 연령, ...)
  • 개인과 속성 사이의 링크 (M : M) (~ 4 천만 행)

풀 덤프 ~ 280MB

상황 :person_id 일부 지역 ( location.attribute_value BETWEEN 3000 AND 7000)에서 성별 ( gender.attribute_value = 1)이며 몇 년 ( bornyear.attribute_value BETWEEN 1980 AND 2000)에 태어나고 눈의 색 ( eyecolor.attribute_value IN (2,3))이있는 모든 개인 ID ( ) 를 선택하려고합니다 .

이것은 나의 쿼리 마녀가 3 ~ 4 분 걸린다 . 최적화하고 싶습니다 :

SELECT person_id
FROM person
    LEFT JOIN attribute location ON location.attribute_type_id = 1 AND location.person_id = person.person_id
    LEFT JOIN attribute gender ON gender.attribute_type_id = 2 AND gender.person_id = person.person_id
    LEFT JOIN attribute bornyear ON bornyear.attribute_type_id = 3 AND bornyear.person_id = person.person_id
    LEFT JOIN attribute eyecolor ON eyecolor.attribute_type_id = 4 AND eyecolor.person_id = person.person_id
WHERE 1
    AND location.attribute_value BETWEEN 3000 AND 7000
    AND gender.attribute_value = 1
    AND bornyear.attribute_value BETWEEN 1980 AND 2000
    AND eyecolor.attribute_value IN (2,3)
LIMIT 100000;

결과:

+-----------+
| person_id |
+-----------+
|       233 |
|       605 |
|       ... |
|   8702599 |
|   8703617 |
+-----------+
100000 rows in set (3 min 42.77 sec)

확장 설명 :

+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
| id | select_type | table    | type   | possible_keys                               | key             | key_len | ref                      | rows    | filtered | Extra                    |
+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
|  1 | SIMPLE      | bornyear | range  | attribute_type_id,attribute_value,person_id | attribute_value | 5       | NULL                     | 1265229 |   100.00 | Using where              |
|  1 | SIMPLE      | location | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.bornyear.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | eyecolor | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.bornyear.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | gender   | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.eyecolor.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | person   | eq_ref | PRIMARY                                     | PRIMARY         | 4       | test1.location.person_id |       1 |   100.00 | Using where; Using index |
+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
5 rows in set, 1 warning (0.02 sec)

프로파일 링 :

+------------------------------+-----------+
| Status                       | Duration  |
+------------------------------+-----------+
| Sending data                 |  3.069452 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.968915 |
| Waiting for query cache lock |  0.000019 |
| Sending data                 |  3.042468 |
| Waiting for query cache lock |  0.000043 |
| Sending data                 |  3.264984 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.823919 |
| Waiting for query cache lock |  0.000038 |
| Sending data                 |  2.863903 |
| Waiting for query cache lock |  0.000014 |
| Sending data                 |  2.971079 |
| Waiting for query cache lock |  0.000020 |
| Sending data                 |  3.053197 |
| Waiting for query cache lock |  0.000087 |
| Sending data                 |  3.099053 |
| Waiting for query cache lock |  0.000035 |
| Sending data                 |  3.064186 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.939404 |
| Waiting for query cache lock |  0.000018 |
| Sending data                 |  3.440288 |
| Waiting for query cache lock |  0.000086 |
| Sending data                 |  3.115798 |
| Waiting for query cache lock |  0.000068 |
| Sending data                 |  3.075427 |
| Waiting for query cache lock |  0.000072 |
| Sending data                 |  3.658319 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.335427 |
| Waiting for query cache lock |  0.000049 |
| Sending data                 |  3.319430 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.496563 |
| Waiting for query cache lock |  0.000029 |
| Sending data                 |  3.017041 |
| Waiting for query cache lock |  0.000032 |
| Sending data                 |  3.132841 |
| Waiting for query cache lock |  0.000050 |
| Sending data                 |  2.901310 |
| Waiting for query cache lock |  0.000016 |
| Sending data                 |  3.107269 |
| Waiting for query cache lock |  0.000062 |
| Sending data                 |  2.937373 |
| Waiting for query cache lock |  0.000016 |
| Sending data                 |  3.097082 |
| Waiting for query cache lock |  0.000261 |
| Sending data                 |  3.026108 |
| Waiting for query cache lock |  0.000026 |
| Sending data                 |  3.089760 |
| Waiting for query cache lock |  0.000041 |
| Sending data                 |  3.012763 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  3.069694 |
| Waiting for query cache lock |  0.000046 |
| Sending data                 |  3.591908 |
| Waiting for query cache lock |  0.000060 |
| Sending data                 |  3.526693 |
| Waiting for query cache lock |  0.000076 |
| Sending data                 |  3.772659 |
| Waiting for query cache lock |  0.000069 |
| Sending data                 |  3.346089 |
| Waiting for query cache lock |  0.000245 |
| Sending data                 |  3.300460 |
| Waiting for query cache lock |  0.000019 |
| Sending data                 |  3.135361 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  2.909447 |
| Waiting for query cache lock |  0.000039 |
| Sending data                 |  3.337561 |
| Waiting for query cache lock |  0.000140 |
| Sending data                 |  3.138180 |
| Waiting for query cache lock |  0.000090 |
| Sending data                 |  3.060687 |
| Waiting for query cache lock |  0.000085 |
| Sending data                 |  2.938677 |
| Waiting for query cache lock |  0.000041 |
| Sending data                 |  2.977974 |
| Waiting for query cache lock |  0.000872 |
| Sending data                 |  2.918640 |
| Waiting for query cache lock |  0.000036 |
| Sending data                 |  2.975842 |
| Waiting for query cache lock |  0.000051 |
| Sending data                 |  2.918988 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  2.943810 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.330211 |
| Waiting for query cache lock |  0.000025 |
| Sending data                 |  3.411236 |
| Waiting for query cache lock |  0.000023 |
| Sending data                 | 23.339035 |
| end                          |  0.000807 |
| query end                    |  0.000023 |
| closing tables               |  0.000325 |
| freeing items                |  0.001217 |
| logging slow query           |  0.000007 |
| logging slow query           |  0.000011 |
| cleaning up                  |  0.000104 |
+------------------------------+-----------+
100 rows in set (0.00 sec)

테이블 구조 :

CREATE TABLE `attribute` (
  `attribute_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `attribute_type_id` int(11) unsigned DEFAULT NULL,
  `attribute_value` int(6) DEFAULT NULL,
  `person_id` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`attribute_id`),
  KEY `attribute_type_id` (`attribute_type_id`),
  KEY `attribute_value` (`attribute_value`),
  KEY `person_id` (`person_id`)
) ENGINE=MyISAM AUTO_INCREMENT=40000001 DEFAULT CHARSET=utf8;

CREATE TABLE `person` (
  `person_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `person_name` text CHARACTER SET latin1,
  PRIMARY KEY (`person_id`)
) ENGINE=MyISAM AUTO_INCREMENT=20000001 DEFAULT CHARSET=utf8;

SSD 및 1GB RAM이있는 DigitalOcean 가상 서버에서 쿼리가 수행되었습니다.

데이터베이스 디자인에 문제가 있다고 가정합니다. 이 상황을 더 잘 디자인 할 제안이 있습니까? 아니면 위의 선택을 조정 하시겠습니까?


4
이것이 EAV 디자인에 지불하는 가격입니다. 당신은에 복합 인덱스를 시도 할 수 있습니다attribute (person_id, attribute_type_id, attribute_value)
mustaccio

1
나는이 인덱스를 추가하려고 할 것입니다 : (attribute_type_id, attribute_value, person_id)(attribute_type_id, person_id, attribute_value)
ypercubeᵀᴹ

5
그리고 InnoDB를 사용하고 MyISAM을 버리십시오. 2015 년입니다. MyiSAM은 오랫동안 죽었습니다.
ypercubeᵀᴹ

2
첫 번째-LEFT 조인을 제거하면 WHERE 조건의 모든 테이블을 사용하여 모든 조인을 INNER 조인으로 효과적으로 전환 할 때 아무런 효과가 없습니다 (최적화자는 이해하고 최적화 할 수는 있지만 더 어렵게 만들지는 않아야합니다) ). 두 번째 일 - 비활성화 쿼리 캐시 당신은 그것 (= 당신이 그것을 테스트하고 당신을 도움이 측정)를 사용하는 특별한 이유가없는 한
jkavalik

2
OT : ORDER BY와 함께 LIMIT를 사용하는 것이 이상하지 않습니까? 이것은 임의의 100000 개의 행을 반환합니까?
ibre5041

답변:


8

에 포함 할 몇 가지 속성을 선택하십시오 person. 단일 열 인덱스가 아닌 복합 인덱스를 사용하여 몇 가지 조합으로 인덱스하십시오.

이것이 본질적으로 EAV 성능 저하를 해결할 수있는 유일한 방법입니다.

자세한 내용은 다음과 같습니다. http-mysql.rjweb.org/doc.php/eav 키-값 테이블 대신 JSON 사용에 대한 제안 포함.


3

에 대한 추가 정보 attribute:

  • (person_id, attribute_type_id, attribute_value)
  • (attribute_type_id, attribute_value, person_id)

설명

현재 디자인에서는 EXPLAIN쿼리에서의 1,265,229 * 4 * 4 * 4 = 80,974,656행 을 검사 할 것으로 예상 됩니다 attribute. 에 대한 복합 색인 을 추가하여이 수를 줄일 수 있습니다 . 이 인덱스를 사용하면 쿼리에만 각각 1 대신에 4 행을 검사합니다 , 하고 .attribute(person_id, attribute_type_id)locationeyecolorgender

당신은 포함하는 인덱스를 확장 할 수 attribute_type_value뿐만 아니라 : (person_id, attribute_type_id, attribute_value). 그러면이 인덱스 가이 쿼리 의 커버링 인덱스로 바뀌어 성능도 향상됩니다.

또한를 (attribute_type_id, attribute_value, person_id)포함하여 포함 인덱스에 인덱스를 추가하면 더 많은 행을 검사해야하는 person_id인덱스를 사용하는 attribute_value것보다 성능이 향상됩니다 . 이 경우 Explain의 첫 번째 단계 인 범위를 선택하십시오 bornyear.

이 두 indeces를 사용하면 다음과 같은 Explain 출력으로 내 시스템에서 쿼리 실행 시간을 ~ 2.0 초에서 ~ 0.2 초로 줄였습니다.

+----+-------------+----------+--------+-------------------------------------+-------------------+---------+--------------------------------+---------+----------+--------------------------+
| id | select_type | table    | type   | possible_keys                       | key               | key_len | ref                            |    rows | filtered | Extra                    |
+----+-------------+----------+--------+-------------------------------------+-------------------+---------+--------------------------------+---------+----------+--------------------------+
|  1 | SIMPLE      | bornyear | range  | person_type_value,type_value_person | type_value_person |       9 |                                | 1861881 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | location | ref    | person_type_value,type_value_person | person_type_value |       8 | bornyear.person_id,const       |       1 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | eyecolor | ref    | person_type_value,type_value_person | person_type_value |       8 | bornyear.person_id,const       |       1 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | gender   | ref    | person_type_value,type_value_person | person_type_value |      13 | bornyear.person_id,const,const |       1 |   100.00 | Using index              |
|  1 | SIMPLE      | person   | eq_ref | PRIMARY                             | PRIMARY           |       4 | bornyear.person_id             |       1 |   100.00 | Using index              |
+----+-------------+----------+--------+-------------------------------------+-------------------+---------+--------------------------------+---------+----------+--------------------------+

1
광범위한 답변과 설명에 감사드립니다. 나는 당신이 언급 한 모든 것을했지만 쿼리는 여전히 ~ 2 분이 걸립니다. 어떤 테이블 유형 (innodb, myisam)을 사용하고 있으며 어떤 정확한 쿼리를 수행 했습니까?
Martin

1
indeces를 추가하는 것 외에는 정확히 동일한 데이터와 정의를 사용 했으므로 MyISAM을 사용했습니다. SELECT person.person_id그렇지 않으면 분명히 실행되지 않기 때문에 쿼리의 첫 번째 줄을 변경했습니다 . ANALYZE TABLE attribute색인을 추가 한 후에 했습니까 ? EXPLAIN질문에 새 결과 를 추가하고 싶을 수도 있습니다.
wolfgangwalther

3

데이터베이스 디자인에 문제가 있다고 가정합니다.

소위 Entity-Attribute-Value 디자인을 사용하고 있는데,이 디자인은 종종 의도적으로 성능이 좋지 않습니다.

이 상황을 더 잘 디자인 할 제안이 있습니까?

이것을 디자인하는 고전적인 관계형 방법은 각 속성에 대해 별도의 테이블을 만드는 것입니다. 일반적으로, 이러한 별도의 테이블을 가질 수 있습니다 location, gender, bornyear, eyecolor.

다음은 개인에 대해 특정 속성이 항상 정의되는지 여부에 따라 다릅니다. 그리고 사람이 하나의 속성 값만 가질 수 있는지 여부. 예를 들어, 일반적으로 그 사람은 성별이 하나뿐입니다. 현재 디자인에서 성별에 대해 다른 값을 가진 같은 사람에 대해 세 개의 행을 추가하는 것을 막을 수있는 것은 없습니다. 성별 값을 1 또는 2로 설정할 수는 없지만 987과 같이 의미가 맞지 않는 숫자로 설정할 수 있으며 데이터베이스에 제약 조건이 없으므로이를 막을 수 없습니다. 그러나 이것은 EAV 디자인으로 데이터 무결성을 유지하는 또 다른 문제입니다.

당신은 항상 사람의 성별을 알고 있다면, 그것은 별도의 테이블에 넣어 거의 의미가 있으며 null이 아닌 열이하는 더 나은 방법입니다 GenderID에서 person의 목록 조회 테이블에 외래 키가 될 것입니다 테이블을, 가능한 모든 성별과 이름. 항상 사람의 성별을 알고 있지만 항상 그런 것은 아니라면이 열을 null로 설정하고 NULL정보를 사용할 수없는 경우이 열을 설정할 수 있습니다. 대부분의 경우 성별을 알 수없는 경우 1 : 1에 gender연결되고 person성별이 알려진 사람에 대해서만 행 이있는 별도의 테이블을 갖는 것이 좋습니다 .

유사한 고려 사항에 적용 eyecolor하고 bornyear- 사람이 두 값을 가질 가능성이있다 eyecolorbornyear.

사람이 속성에 대해 여러 값을 가질 수 있다면 별도의 테이블에 넣을 것입니다. 예를 들어, 사람이 여러 주소 (집, 직장, 우편, 휴일 등)를 갖는 것은 드문 일이 아니므로 모두 표에 나열합니다 location. 테이블 personlocation1 : M 연결됩니다.


아니면 위의 선택을 조정 하시겠습니까?

EAV 디자인을 사용하는 경우 최소한 다음을 수행해야합니다.

  • 설정 열 attribute_type_id, attribute_value, person_idNOT NULL.
  • attribute.person_id와 연결 되는 외래 키를 설정하십시오 person.person_id.
  • 세 개의 열에 하나의 인덱스를 만듭니다 (attribute_type_id, attribute_value, person_id). 여기서는 열 순서가 중요합니다.
  • 내가 아는 한 MyISAM은 외래 키를 존중하지 않으므로 사용하지 말고 대신 InnoDB를 사용하십시오.

이런 식으로 쿼리를 작성합니다. 조인 INNER대신 사용 LEFT하고 각 속성에 대해 하위 쿼리를 명시 적으로 작성하여 옵티마이 저가 인덱스를 사용할 수있는 모든 기회를 제공하십시오.

SELECT person.person_id
FROM
    person
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 1
            AND location.attribute_value BETWEEN 3000 AND 7000
    ) AS location ON location.person_id = person.person_id
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 2
            AND location.attribute_value = 1
    ) AS gender ON gender.person_id = person.person_id
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 3
            AND location.attribute_value BETWEEN 1980 AND 2000
    ) AS bornyear ON bornyear.person_id = person.person_id
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 4
            AND location.attribute_value IN (2, 3)
    ) AS eyecolor ON eyecolor.person_id = person.person_id
LIMIT 100000;

또한로 테이블을 분할 하는 것이 좋습니다.attributeattribute_type_id


성능주의 : JOIN ( SELECT ... )제대로 최적화되지 않습니다. JOINing직접 테이블에 더 잘 작동하지만 여전히 문제가 있습니다.
Rick James

3

충분한 해결책을 찾았기를 바랍니다. 이 기사 에서 영감을 얻었습니다 .

짧은 답변:

  1. 모든 속성을 가진 1 개의 테이블을 만들었습니다. 하나의 속성에 대한 하나의 열. 플러스 기본 키 열.
  2. 속성 값은 전체 텍스트 검색을 위해 CSV 형식의 텍스트 셀에 저장됩니다.
  3. 전체 텍스트 인덱스를 만들었습니다. 그 전에 섹션 ft_min_word_len=1에서 [mysqld]( MyISAM) 및 파일 innodb_ft_min_token_size=1에서 (InnoDb) 를 설정하는 것이 중요합니다 .mysql my.cnf서비스를 다시 시작하십시오.
  4. 검색 예 : SELECT * FROM person_index WHERE MATCH(attribute_1) AGAINST("123 456 789" IN BOOLEAN MODE) LIMIT 1000여기서 123, 456789사람이 연결해야하는 ID입니다 attribute_1. 이 쿼리는 1 초 미만이 걸렸습니다.

자세한 답변 :

1 단계. 전체 텍스트 인덱스가있는 테이블 만들기 InnoDb는 MySQL 5.7에서 전체 텍스트 인덱스를 지원하므로 5.5 또는 5.6을 사용하는 경우 MyISAM을 사용해야합니다. 때로는 InnoDb보다 FT 검색이 더 빠릅니다.

CREATE TABLE `person_attribute_ft` (
  `person_id` int(11) NOT NULL,
  `attr_1` text,
  `attr_2` text,
  `attr_3` text,
  `attr_4` text,
  PRIMARY KEY (`person_id`),
  FULLTEXT KEY `attr_1` (`attr_1`),
  FULLTEXT KEY `attr_2` (`attr_2`),
  FULLTEXT KEY `attr_3` (`attr_3`),
  FULLTEXT KEY `attr_4` (`attr_4`),
  FULLTEXT KEY `attr_12` (`attr_1`,`attr_2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

단계 2. EAV (entity-attribute-value) 테이블에서 데이터를 삽입하십시오. 예를 들어 문제의 진술은 1 개의 간단한 SQL로 수행 할 수 있습니다.

INSERT IGNORE INTO `person_attribute_ft`
SELECT
    p.person_id,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 1 AND a.person_id = p.person_id LIMIT 10) attr_1,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 2 AND a.person_id = p.person_id LIMIT 10) attr_2,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 3 AND a.person_id = p.person_id LIMIT 10) attr_3,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 4 AND a.person_id = p.person_id LIMIT 10) attr_4
FROM person p

결과는 다음과 같아야합니다.

mysql> select * from person_attribute_ft limit 10;
+-----------+--------+--------+--------+--------+
| person_id | attr_1 | attr_2 | attr_3 | attr_4 |
+-----------+--------+--------+--------+--------+
|         1 | 541    | 2      | 1927   | 3      |
|         2 | 2862   | 2      | 1939   | 4      |
|         3 | 6573   | 2      | 1904   | 2      |
|         4 | 2432   | 1      | 2005   | 2      |
|         5 | 2208   | 1      | 1995   | 4      |
|         6 | 8388   | 2      | 1973   | 1      |
|         7 | 107    | 2      | 1909   | 4      |
|         8 | 5161   | 1      | 2005   | 1      |
|         9 | 8022   | 2      | 1953   | 4      |
|        10 | 4801   | 2      | 1900   | 3      |
+-----------+--------+--------+--------+--------+
10 rows in set (0.00 sec)

단계 3. 다음과 같은 쿼리가있는 테이블에서 선택하십시오.

mysql> SELECT SQL_NO_CACHE *
    -> FROM `person_attribute_ft`
    -> WHERE 1 AND MATCH(attr_1) AGAINST ("3000 3001 3002 3003 3004 3005 3006 3007" IN BOOLEAN MODE)
    -> AND MATCH(attr_2) AGAINST ("1" IN BOOLEAN MODE)
    -> AND MATCH(attr_3) AGAINST ("1980 1981 1982 1983 1984" IN BOOLEAN MODE)
    -> AND MATCH(attr_4) AGAINST ("2,3" IN BOOLEAN MODE)
    -> LIMIT 10000;
+-----------+--------+--------+--------+--------+
| person_id | attr_1 | attr_2 | attr_3 | attr_4 |
+-----------+--------+--------+--------+--------+
|     12131 | 3002   | 1      | 1982   | 2      |
|     51315 | 3007   | 1      | 1984   | 2      |
|    147283 | 3001   | 1      | 1984   | 2      |
|    350086 | 3005   | 1      | 1982   | 3      |
|    423907 | 3004   | 1      | 1982   | 3      |
... many rows ...
|   9423907 | 3004   | 1      | 1982   | 3      |
|   9461892 | 3007   | 1      | 1982   | 2      |
|   9516361 | 3006   | 1      | 1980   | 2      |
|   9813933 | 3005   | 1      | 1982   | 2      |
|   9986892 | 3003   | 1      | 1981   | 2      |
+-----------+--------+--------+--------+--------+
90 rows in set (0.17 sec)

쿼리는 모든 행을 선택합니다.

  • 다음에서이 ID 중 하나 이상을 일치시킵니다 attr_1.3000, 3001, 3002, 3003, 3004, 3005, 3006 or 3007
  • 동시에 매칭에 1있는 attr_2(열이 나타내는 성별 이 용액을 정의한다면, 그것이 있어야하므로 smallint(1)간단한 지수 등)
  • 그리고 동시에 1980, 1981, 1982, 1983 or 1984in 중 하나 이상과 일치attr_3
  • 동시에 매칭에 2또는 3안에attr_4

결론:

이 솔루션이 많은 상황에 완벽하지는 않지만 EAV 테이블 디자인을위한 훌륭한 대안으로 사용될 수 있다는 것을 알고 있습니다.

누군가에게 도움이되기를 바랍니다.


1
이 디자인이 복합 인덱스가있는 원래 디자인보다 성능이 우수하지는 않습니다. 그것들을 비교하기 위해 어떤 테스트를 했습니까?
ypercubeᵀᴹ

0

적절한 쿼리 색인 힌트를 사용해보십시오.

MySQL 인덱스 힌트


1
힌트는 한 버전의 쿼리에 도움이 될 수 있지만 다른 버전은 손상 될 수 있습니다. 옵티마이 저는 가장 바람직하지 않은 행을 걸러 낼 수 있기 때문에 bornyear를 최고의 첫 번째 테이블로 선택했습니다.
Rick James
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.