MySQL에 배열을 저장하는 방법은 무엇입니까?


118

MySQL에는 두 개의 테이블이 있습니다. Table Person에는 다음 열이 있습니다.

id | name | fruits

fruits열에 null 또는 ( '사과', '오렌지', '바나나') 또는 ( '딸기') 등 두 번째 테이블이 표 열매와 다음의 세 개의 열이 같은 문자열의 배열을 보유 할 수있다 :

____________________________
fruit_name | color  | price
____________________________
apple      | red    | 2
____________________________
orange     | orange | 3
____________________________
...,...

그렇다면 두 번째 테이블 fruits의 fruit_name열에서 값을 가져 오는 문자열 배열을 보유 할 수 있도록 첫 번째 테이블 의 열을 어떻게 디자인해야 합니까? MySQL에는 배열 데이터 유형이 없으므로 어떻게해야합니까?



1
오렌지, 2, 1, 장미, 2, 1 등 별도의 항목으로 추가하는 것은 어떻습니까? 그런 다음 쿼리를 사용하여 마치 배열 인 것처럼 처리 할 수 ​​있습니다.
— Sai

@JanusTroelsen : DB 읽기 / 쓰기를 위해 PHP를 사용하지 않습니다. 그렇다면 보편적 인 방법이 있습니까?
— tonga

1
@tonga는 내 바이올린을 확인하십시오.
— echo_Me

답변:


163

이를 수행하는 적절한 방법은 JOIN쿼리에서 여러 테이블과 테이블을 사용하는 것입니다.

예를 들면 :

CREATE TABLE person (
`id` INT NOT NULL PRIMARY KEY,
`name` VARCHAR(50)
);

CREATE TABLE fruits (
`fruit_name` VARCHAR(20) NOT NULL PRIMARY KEY,
`color` VARCHAR(20),
`price` INT
);

CREATE TABLE person_fruit (
`person_id` INT NOT NULL,
`fruit_name` VARCHAR(20) NOT NULL,
PRIMARY KEY(`person_id`, `fruit_name`)
);

이 person_fruit테이블에는 사람이 관련된 각 과일에 대해 하나의 행이 포함되어 있으며 person및 fruits테이블을 효과적으로 연결합니다 . IE

1 | "banana"
1 | "apple"
1 | "orange"
2 | "straberry"
2 | "banana"
2 | "apple"

사람과 모든 과일을 회수하려면 다음과 같이 할 수 있습니다.

SELECT p.*, f.*
FROM person p
INNER JOIN person_fruit pf
ON pf.person_id = p.id
INNER JOIN fruits f
ON f.fruit_name = pf.fruit_name

4
세 번째 테이블은 Person과 Fruit 간의 연결 테이블입니다. 그래서 사람이 100 개의 과일을 가지고 있다면. 세 번째 테이블에 100 개의 행을 만들어야합니다. 맞죠? 이것이 효율적입니까?
— tonga

1
@tonga 정확히, 100 개의 행 각각은 동일 person_id하지만 다른 fruit_name. 이것은 Janus의 대답에서 이론을 효과적으로 구현 한 것입니다.
— Bad Wolf

1
두 테이블 간의 관계가 세 번째 테이블에 저장되어야한다는 것이 항상 사실입니까? 두 테이블의 기본 키를 저장하여 관계를 찾는 쿼리를 수행 할 수 있습니까?
— tonga

2
예, 이것이 지금 예제 설정 방법입니다. 그 사람에 대한 모든 정보는 person테이블에 있어야하며, 테이블에있는 과일에 대한 모든 정보, fruits테이블에있는 특정 사람과 특정 과일 사이의 관계에 대한 정보가 있어야 person_fruit합니다. 이 예에서는 추가 정보가 없기 때문에 person_fruit테이블은 person및 fruits테이블 의 기본 키인 두 개의 열뿐입니다 . 특정 과일의 양은 person_fruit테이블에 들어갈 수있는 다른 예입니다 .
— Bad Wolf

2
아닌를 사용하는 것이 더 있을까 INT에 키 fruits만이 가지고 INT있는을person_fruit ? 따라서 이름은 나중에 변경할 수 있으며에서 fruits보다 행이 많지 않은 경우 공간이 더 적게 필요합니다 person_fruit.
— 12431234123412341234123

58

SQL에 배열이없는 이유는 대부분의 사람들이 실제로 필요하지 않기 때문입니다. 관계형 데이터베이스 (정확히 SQL)는 관계를 사용하여 작동하며 대부분의 경우 각 "정보 비트"에 테이블의 한 행을 할당하는 것이 가장 좋습니다. 예를 들어, "여기에있는 항목의 목록을 원합니다."라고 생각하는 경우 대신 한 테이블의 행을 다른 테이블의 행과 연결하여 새 테이블을 만듭니다. [1] 이렇게하면 M : N 관계를 나타낼 수 있습니다. 또 다른 장점은 이러한 링크가 연결된 항목을 포함하는 행을 복잡하게 만들지 않는다는 것입니다. 그리고 데이터베이스는 이러한 행을 인덱싱 할 수 있습니다. 배열은 일반적으로 인덱싱되지 않습니다.

관계형 데이터베이스가 필요하지 않은 경우 키-값 저장소 등을 사용할 수 있습니다.

데이터베이스 정규화 에 대해 읽어보십시오 . 황금률은 "[모든] 키가 아닌 [속성]은 키에 대한 사실, 전체 키 및 키만 제공해야합니다."입니다. 배열은 너무 많은 일을합니다. 여러 팩트가 있으며 주문을 저장합니다 (관계 자체와 관련 없음). 그리고 성능이 좋지 않습니다 (위 참조).

사람 테이블이 있고 사람들이 전화를 거는 테이블이 있다고 상상해보십시오. 이제 각 사람 행에 그의 전화 목록을 만들 수 있습니다. 그러나 모든 사람은 다른 많은 것들과 많은 다른 관계를 가지고 있습니다. 그것은 내 사람 테이블이 그가 연결된 모든 단일 항목에 대한 배열을 포함해야 함을 의미합니까? 아니요, 그것은 그 사람 자체의 속성이 아닙니다.

[1] : 연결 테이블에 두 개의 열 (각 테이블의 기본 키) 만 있으면 괜찮습니다! 하지만 관계 자체에 추가 속성이있는 경우이 테이블에서 열로 표시되어야합니다.


2
고마워 야누스. 말이 되네요. 이제 MySQL이 열에서 배열 유형을 지원하지 않는 이유를 이해합니다.
— tonga

2
@Sai-내가하는 일에 대해 정말 NoSQL 솔루션이 필요합니까?
— tonga

1
좋습니다. 예를 들어 센서에서 수집 한 2D 데이터와 같이 필드에 수천 개의 요소로 구성된 숫자 배열이 포함 된 테이블이있는 경우 NoSQL DB를 사용하는 것이 훨씬 낫습니까?
— tonga

5
@tonga : 데이터의 양은 사용할 db 유형을 결정하지 않으며 데이터의 특성에 따라 결정됩니다. 관계가 없으면 관계형 데이터베이스에서 필요하지 않습니다. 그러나 이것이 업계 표준이므로 관계형 기능을 사용하지 않고 그대로 유지할 수 있습니다. 대부분의 데이터는 어떤 식 으로든 관계형입니다! 관계형 데이터베이스를 비정규 화하거나 키-값 저장소를 사용하는 일반적인 이유는 성능상의 이유 때문입니다. 그러나 이러한 문제는 수백만 개의 행이있을 때만 발생합니다! 너무 일찍 최적화하지 마십시오! SQL db를 사용하는 것이 좋습니다 (PostgreSQL을 권장합니다). 문제가 있으면 물어보십시오.
— Janus Troelsen 2013-06-28

2
PostgreSQL에는 또한 키-값 저장소가 내장되어 있으므로 적합하지 않은 경우 관계형 모델에서 더 쉽게 이동할 수 있습니다.
— Janus Troelsen 2013 년

50

MySQL 5.7은 이제 JSON 데이터 유형을 . 이 새로운 데이터 유형은 목록, 사전 등 복잡한 데이터를 저장하는 편리한 새로운 방법을 제공합니다.

즉, rray는 데이터베이스를 잘 매핑하지 못하기 때문에 객체 관계형 맵이 매우 복잡 할 수 있습니다. 역사적으로 사람들은 목록 / 배열을 설명하는 테이블을 만들고 각 값을 자체 레코드로 추가하여 MySQL에 목록 / 배열을 저장했습니다. 테이블에는 2 개 또는 3 개의 열만 있거나 더 많은 열을 포함 할 수 있습니다. 이러한 유형의 데이터를 저장하는 방법은 실제로 데이터의 특성에 따라 다릅니다.

예를 들어, 목록에 정적 또는 동적 항목 수가 포함되어 있습니까? 목록이 작게 유지됩니까? 아니면 수백만 개의 레코드로 늘어날 것으로 예상됩니까? 이 테이블에 많은 읽기가 있습니까? 많은 쓰기? 많은 업데이트? 이는 데이터 콜렉션을 저장하는 방법을 결정할 때 고려해야 할 모든 요소입니다.

또한 Key : Value 데이터 저장소 / Cassandra, MongoDB, Redis 등과 같은 문서 저장소도 좋은 솔루션을 제공합니다. 데이터가 실제로 저장되는 위치 만 알고 있어야합니다 (디스크 또는 메모리에 저장되는 경우). 모든 데이터가 동일한 데이터베이스에 있어야하는 것은 아닙니다. 일부 데이터는 관계형 데이터베이스에 잘 매핑되지 않으며 다른 곳에 저장해야하는 이유가 있거나 메모리 내 키 : 값 데이터베이스를 디스크 어딘가에 저장된 데이터에 대한 핫 캐시로 사용하거나 임시 저장소로 사용할 수 있습니다. 세션과 같은 것들을 위해.


43

고려해야 할 추가 사항으로 Postgres에 어레이를 저장할 수 있습니다.


6
추가 참고 사항 : 색인화 될 수 있으므로 배열에 특정 값이 있는지 확인하는 쿼리가 매우 빠를 수 있습니다. 복잡한 JSON 유형도 마찬가지입니다.
— timetofly

5
이것은 어떤 식 으로든 질문에 답하지 않습니다. OP는 MySQL에 대해 물었습니다.
— jhpratt

1
Postgres에서 ArrayField를 사용하고 해당 열에 전체 값 목록 (예 : 고정 태그 목록)이있는 경우 GIN 인덱스를 만들 수 있습니다. 그러면 해당 열에 대한 쿼리 속도가 크게 빨라집니다.
— lumos42

25

MySQL에서는 JSON 유형을 사용합니다.

위의 답변과 달리 SQL 표준에는 거의 20 년 동안 배열 유형이 포함되어 있습니다. MySQL이 구현하지 않은 경우에도 유용합니다.

그러나 귀하의 예에서는 person과 fruit, 그리고 person_fruit로 테이블을 조인하는 세 개의 테이블을 만들고 싶을 것입니다.

DROP TABLE IF EXISTS person_fruit;
DROP TABLE IF EXISTS person;
DROP TABLE IF EXISTS fruit;

CREATE TABLE person (
  person_id   INT           NOT NULL AUTO_INCREMENT,
  person_name VARCHAR(1000) NOT NULL,
  PRIMARY KEY (person_id)
);

CREATE TABLE fruit (
  fruit_id    INT           NOT NULL AUTO_INCREMENT,
  fruit_name  VARCHAR(1000) NOT NULL,
  fruit_color VARCHAR(1000) NOT NULL,
  fruit_price INT           NOT NULL,
  PRIMARY KEY (fruit_id)
);

CREATE TABLE person_fruit (
  pf_id     INT NOT NULL AUTO_INCREMENT,
  pf_person INT NOT NULL,
  pf_fruit  INT NOT NULL,
  PRIMARY KEY (pf_id),
  FOREIGN KEY (pf_person) REFERENCES person (person_id),
  FOREIGN KEY (pf_fruit) REFERENCES fruit (fruit_id)
);

INSERT INTO person (person_name)
VALUES
  ('John'),
  ('Mary'),
  ('John'); -- again

INSERT INTO fruit (fruit_name, fruit_color, fruit_price)
VALUES
  ('apple', 'red', 1),
  ('orange', 'orange', 2),
  ('pineapple', 'yellow', 3);

INSERT INTO person_fruit (pf_person, pf_fruit)
VALUES
  (1, 1),
  (1, 2),
  (2, 2),
  (2, 3),
  (3, 1),
  (3, 2),
  (3, 3);

그 사람을 과일 배열과 연관 시키려면보기를 사용하면됩니다.

DROP VIEW IF EXISTS person_fruit_summary;
CREATE VIEW person_fruit_summary AS
  SELECT
    person_id                                                                                              AS pfs_person_id,
    max(person_name)                                                                                       AS pfs_person_name,
    cast(concat('[', group_concat(json_quote(fruit_name) ORDER BY fruit_name SEPARATOR ','), ']') as json) AS pfs_fruit_name_array
  FROM
    person
    INNER JOIN person_fruit
      ON person.person_id = person_fruit.pf_person
    INNER JOIN fruit
      ON person_fruit.pf_fruit = fruit.fruit_id
  GROUP BY
    person_id;

보기에는 다음 데이터가 표시됩니다.

+---------------+-----------------+----------------------------------+
| pfs_person_id | pfs_person_name | pfs_fruit_name_array             |
+---------------+-----------------+----------------------------------+
|             1 | John            | ["apple", "orange"]              |
|             2 | Mary            | ["orange", "pineapple"]          |
|             3 | John            | ["apple", "orange", "pineapple"] |
+---------------+-----------------+----------------------------------+

5.7.22에서는 JSON_ARRAYAGG를 사용 하려고합니다. 문자열에서 배열을 함께 해킹하는 대신 것입니다.


2

데이터베이스 필드 유형 BLOB를 사용하여 배열을 저장하십시오.

참조 : http://us.php.net/manual/en/function.serialize.php

반환 값

어디에나 저장할 수있는 값의 바이트 스트림 표현을 포함하는 문자열을 반환합니다.

이것은 널 바이트를 포함 할 수있는 이진 문자열이며 저장 및 처리해야합니다. 예를 들어, serialize () 출력은 일반적으로 CHAR 또는 TEXT 필드가 아닌 데이터베이스의 BLOB 필드에 저장되어야합니다.


-4

group_Concat을 사용하여 배열을 저장할 수 있습니다.

 INSERT into Table1 (fruits)  (SELECT GROUP_CONCAT(fruit_name) from table2)
 WHERE ..... //your clause here

여기 바이올린 의 예


4
잘 설명되지 않았습니다. 잘못된 테이블 이름.
— Martin F
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.