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


118

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

id | name | fruits

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

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

그렇다면 두 번째 테이블 fruitsfruit_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테이블에는 사람이 관련된 각 과일에 대해 하나의 행이 포함되어 있으며 personfruits테이블을 효과적으로 연결합니다 . 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테이블은 personfruits테이블 의 기본 키인 두 개의 열뿐입니다 . 특정 과일의 양은 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.