데이터베이스 열에 구분 목록을 저장하는 것이 실제로 그렇게 나쁩니 까?


363

확인란 세트가있는 웹 양식을 상상해보십시오 (일부 또는 모두 선택할 수 있음). 데이터베이스 테이블의 한 열에 저장된 값을 쉼표로 구분 된 값 목록에 저장하기로했습니다.

이제 올바른 해결책은 두 번째 테이블을 만들고 데이터베이스를 올바르게 정규화하는 것입니다. 쉬운 솔루션을 구현하는 것이 더 빨랐으며 너무 많은 시간을 들이지 않고도 해당 애플리케이션의 개념 증명을 신속하게 원했습니다.

절약 된 시간과 간단한 코드가 제 상황에서 가치가 있다고 생각했습니다. 이것이 방어적인 디자인 선택입니까, 아니면 처음부터 정규화해야합니까?

좀 더 많은 맥락에서 이것은 공유 폴더에 저장된 Excel 파일을 본질적으로 대체하는 작은 내부 응용 프로그램입니다. 나는 또한 프로그램을 정리하고 더 유지하기 쉽게 만드는 것에 대해 생각하고 있기 때문에 묻습니다. 거기에 내가 완전히 만족하지 않는 것들이 있습니다. 그중 하나 가이 질문의 주제입니다.


21
이 경우 데이터베이스를 귀찮게하는 이유는 무엇입니까?
thavan

6
@thavan과 동의했습니다. 개념 증명을 위해 데이터를 저장해야하는 이유는 무엇입니까? 증명이 완료되면 데이터베이스를 올바르게 추가하십시오. 개념 증명을 위해 가벼운 작업을 수행하면 나중에 만들지 않아도됩니다.
Jeff Davis

1
Postgres에서 쉼표로 구분 된 목록보다 배열 열을 선호해야합니다. 최소한 적절한 데이터 유형을 보장하고 구분 기호를 실제 데이터와 구별하는 데 아무런 문제가 없으며 효율적으로 색인을 생성 할 수 있습니다.
a_horse_with_no_name 6

답변:


567

단일 열에 저장된 반복되는 값 그룹으로 인해 첫 번째 정규형 을 위반하는 것 외에도 쉼표로 구분 된 목록에는 다른 여러 가지 실질적인 문제가 있습니다.

  • 각 값이 올바른 데이터 유형인지 보장 할 수 없음 : 1,2,3, 바나나, 5 를 막을 방법 없음
  • 외래 키 제약 조건을 사용하여 값을 조회 테이블에 연결할 수 없습니다. 참조 무결성을 강화할 방법이 없습니다.
  • 독창성을 강요 할 수 없음 : 1,2,3,3,3,5
  • 전체 목록을 가져 오지 않고 목록에서 값을 삭제할 수 없습니다.
  • 문자열 열에 맞는 것보다 더 긴 목록을 저장할 수 없습니다.
  • 목록에서 지정된 값을 가진 모든 엔티티를 검색하기가 어렵습니다. 비효율적 인 테이블 스캔을 사용해야합니다. 예를 들어 MySQL과 같은 정규식에 의존해야 할 수도 있습니다.
    idlist REGEXP '[[:<:]]2[[:>:]]'*
  • 목록의 요소를 계산하거나 다른 집계 쿼리를 수행하기가 어렵습니다.
  • 참조하는 조회 테이블에 값을 결합하기가 어렵습니다.
  • 정렬 된 순서로 목록을 가져 오기가 어렵습니다.

이러한 문제를 해결하려면 수많은 응용 프로그램 코드를 작성하여 RDBMS가 이미 훨씬 더 효율적으로 제공 하는 기능을 다시 개발해야합니다 .

쉼표로 구분 된 목록은이 책을 내 책의 첫 번째 장으로 만들 정도로 충분히 잘못되었습니다. SQL Antipatterns : 데이터베이스 프로그래밍의 함정 피하기 .

비정규 화를 사용해야 할 때가 있지만 @OMG Ponies가 언급했듯이 예외는 예외입니다. 관계형이 아닌 "최적화"는 다른 유형의 데이터 사용을 희생하여 한 가지 유형의 쿼리에 도움이되므로 비정규 화를 수행 할 수 있도록 특별히 처리해야하는 쿼리를 알고 있어야합니다.


* MySQL의 8.0 더 이상이 단어 경계를 표현 구문을 지원하지 않습니다.


8
ARRAY (모든 데이터 유형)는 예외를 수정할 수 있습니다. PostgreSQL을 확인하십시오 : postgresql.org/docs/current/static/arrays.html (@Bill : 훌륭한 책, 개발자 나 dba는 반드시 읽어야합니다)
Frank Heikens

4
+1 bill Karwin 대단한 답변! 사랑스러운 간결한 글 머리 기호. 그것은 훌륭한 책처럼 보입니다. 커버를 너무 좋아 +1 NullUserException. 플랫 파일 텍스트 기반 시스템을 대체하기 위해 MySQL 데이터베이스의 스키마를 설계하는 중입니다. 지금까지 몇 가지 딜레마가 발생했습니다. 따라서이 책은 구매할 가치가 있습니다.
therobyouknow

2
pragprog.com 사이트도 멋지게 보입니다 : 멋진 스타일, 레이아웃, 사용자 친화적 인 청소. 이것은 상당히 새롭습니다. 과거에는 eBook을 구입할 수 없었습니다. 추신. 나는 그들이 저자와 어떤 관련이 있는지 작동하지 않습니다. 나는 좋은 제품, 서비스 및 도움을 축하합니다.
therobyouknow

2
진지하게, 나는 당신의 목록에 추가하고 싶습니다 : 검색하기 어려움. "2"를 포함하는 모든 레코드를 원한다고 가정하십시오. 물론 foobar = '2'만 검색 할 수는 없습니다. 다른 값이 있으면 놓칠 수 있기 때문입니다. '% 2 %'와 같이 foobar를 검색 할 수 없습니다. 12와 28 등에서 잘못된 조회가 발생하기 때문입니다. 2는 목록의 첫 번째 또는 마지막 요소 일 수 있으므로 쉼표 중 하나만 있으므로 '%, 2, %'와 같은 foobar를 검색 할 수 없습니다.
Jay

2
나는 그것이 권장되지는 않지만 악마 옹호자를 연주한다는 것을 알고 있습니다 : 고유성과 데이터 유형을 처리하는 UI가 있거나 (그렇지 않으면 오류가 있거나 오작동 할 수 있음) UI가 떨어지면 어쨌든 그것을 제거 할 수있는 드라이버 테이블이 있습니다. '% P %'와 같은 필드를 사용할 수 있고, P, R, S, T와 같은 필드를 사용할 수 있으며, 계산은 중요하지 않으며 정렬은 중요하지 않습니다. UI에 따라 값을 split [] 할 수 있습니다. 예를 들어 다른 테이블로 이동하지 않고도 가장 일반적인 시나리오에서 드라이버 테이블의 목록에있는 확인란을 확인할 수 있습니다.
jmcclure

44

"한가지 이유는 게으름이었다".

알람 벨이 울립니다. 이와 같은 일을해야하는 유일한 이유는 "올바른 방법"으로하는 방법을 알고 있지만 그렇게하지 않는 확실한 이유가 있다는 결론에 도달했기 때문입니다.

이렇게 말한 경우 :이 방법으로 저장하려는 데이터가 쿼리 할 필요가없는 데이터 인 경우 선택한 방식으로 데이터를 저장하는 경우가있을 수 있습니다.

(일부 사용자는 앞의 단락에서“앞으로 어떤 요구 사항이 추가 될지 알 수 없습니다”라는 진술에 이의를 제기 할 수 있습니다. 당신 앞에 있습니다.)


외래 키 제약 조건을 설정하지 않거나 단일 필드에 목록을 저장하는 것과 같은 문제에 직면 할 때 "내 디자인이 사용자보다 유연합니다"라고 말하는 사람들이 항상 있습니다. 나에게 유연성 (이러한 경우) == 징계 없음 == 게으름.
foresightyj

41

SO 요청에 대한 수많은 질문이 있습니다.

  • 쉼표로 구분 된 목록에서 특정 값의 개수를 얻는 방법
  • 쉼표로 구분 된 목록에서 동일한 2 / 3 / etc 특정 값만있는 레코드를 얻는 방법

쉼표로 구분 된 목록의 또 다른 문제는 값이 일관성을 유지하는 것입니다. 텍스트를 저장하면 오타가 발생할 가능성이 있습니다.

이는 모두 비정규 화 된 데이터의 증상이며 항상 정규화 된 데이터를 모델링해야하는 이유를 강조합니다. 비정규 화 요구가 실제로 제시 될 때 적용되는 쿼리 최적화 일 수 있습니다 .


19

일반적으로 프로젝트 요구 사항을 충족하면 방어 할 수 있습니다. 이것은 사람들이 당신의 결정에 동의하거나 방어하기를 원한다는 의미는 아닙니다 ...

일반적으로 이러한 방식으로 데이터를 저장하는 것은 차선책이므로 (예 : 효율적인 쿼리를 수행하기가 더 어렵습니다) 양식에서 항목을 수정하면 유지 관리 문제가 발생할 수 있습니다. 아마도 중간 접지를 발견하고 대신 비트 플래그 세트를 나타내는 정수를 사용했을 수 있습니까?


10

그렇습니다, 나는 그것이 정말로 나쁘다고 말합니다. 그것은 선택의 여지가 있지만 그것이 정확하거나 좋지는 않습니다.

첫 번째 정상적인 형태를 break습니다.

두 번째 비판은 유효성 검사 나 바인딩없이 원시 입력 결과를 데이터베이스에 직접 넣는 것이 SQL 인젝션 공격에 노출된다는 것입니다.

게으름과 SQL 지식의 부족이라고 부르는 것은 신생 생물이 만들어내는 것입니다. 시간을내어 제대로하고 배우는 기회로 여길 것을 권합니다.

또는 그대로두고 SQL 주입 공격에 대한 고통스러운 교훈을 배우십시오.


19
이 질문에서 그가 SQL 주입에 취약하다는 것을 알 수있는 것은 없습니다. SQL 인젝션 및 데이터베이스 정규화는 직교적인 주제이며 인젝션에 대한 분석은 질문과 관련이 없습니다.
Hammerite

5
@Paul : 길을 건너기 전에 두 가지 방법을 모두 보지 못하면 버스에서 똑같은 태도로 버스를 치게 될 수도 있지만 경고하지 않았습니다. 편집 : 나는 당신 이이 대답의 포스터, 내 실수라고 생각했습니다.
Hammerite

1
@Hammerite-버스에 대한 외삽은 어리 석습니다.
duffymo

4
그렇습니다. 이 말의 어리 석음은 내가 만들고있는 요점을 보여줍니다. 즉, 경고해야 할 이유가없는 것에 대해 경고하는 것은 말이되지 않습니다.
Hammerite

1
그래, 난 참조. 버스에 대한 경고가 더 많은 이유가 있다고 생각합니다.
duffymo

7

글쎄, 지금 4 년 이상 SQL Server의 NTEXT 열에서 키 / 값 쌍 탭으로 구분 된 목록을 사용 해 왔으며 작동합니다. 쿼리의 유연성을 잃지 만 반면에 키 값 쌍을 유지 / 제거하는 라이브러리가 있다면 그렇게 나쁜 생각이 아닙니다.


13
아니요, 끔찍한 생각입니다. 이를 극복했지만 몇 분의 개발 시간으로 인해 쿼리 성능, 유연성 및 코드 유지 관리 비용이 크게 증가했습니다.
Paul Tomblin

5
폴 동의합니다 그러나 내가 말했듯이 나는 특정 목적을 위해 사용했는지, 그것은 당신이 많은 종류의 양식을 가진 데이터 입력 작업을위한 것입니다. NHibernate를 배웠으므로 이제 디자인을 수정하고 있지만 ASP.NET에서 양식을 디자인하고 텍스트 상자 ID를 키 / 값 쌍의 키로 사용할 수있는 유연성이 필요했습니다.
Raj

28
downvotes를 반박하기 위해 +1. 4 년 동안 앱을 유지 관리 한 사람에게 유지 관리 문제를 알리는 것은 다소 어려울 수 있습니다. sw 개발에는 "끔찍한"아이디어가 거의 없습니다. 주로 적용 가능성이 매우 제한된 아이디어 일뿐입니다. 사람들에게 그 한계에 대해 경고하는 것이 합리적이지만, 그것을 행하고 그것을 통해 살았던 사람들을 쫓는 것은 내가없이 할 수있는 더 성스러운 태도로 나를 때리게합니다.
Mark Brackett

7

다중 값 열이 필요했습니다 .xml 필드로 구현할 수 있습니다.

필요에 따라 쉼표로 구분하여 변환 할 수 있습니다.

XQuery를 사용하여 SQL Server에서 XML 목록을 조회 .

xml 필드이기 때문에 일부 문제를 해결할 수 있습니다.

CSV 사용 : 각 값이 올바른 데이터 유형인지 보장 할 수 없음 : 1,2,3, 바나나, 5를 막을 방법 없음

XML을 사용하면 태그의 값을 올바른 유형으로 만들 수 있습니다.


CSV 사용 : 외래 키 제약 조건을 사용하여 값을 조회 테이블에 연결할 수 없습니다. 참조 무결성을 강화할 방법이 없습니다.

XML로 : 여전히 문제


CSV 사용 : 고유성을 강제 할 수 없음 : 1,2,3,3,3,5를 방지 할 수있는 방법이 없습니다

XML로 : 여전히 문제


CSV 사용 : 전체 목록을 가져 오지 않고 목록에서 값을 삭제할 수 없습니다.

XML : 단일 항목을 제거 할 수 있습니다


CSV 사용 : 목록에서 지정된 값을 가진 모든 엔티티를 검색하기가 어렵습니다. 비효율적 인 테이블 스캔을 사용해야합니다.

XML : XML 필드를 색인 할 수 있습니다


CSV 사용 : 목록에서 요소를 계산하기 어렵거나 다른 집계 쿼리를 수행합니다. **

XML로 : 특별히 어렵지 않은


CSV : 값을 참조하는 조회 테이블에 결합하기가 어렵습니다. **

XML로 : 특별히 어렵지 않은


CSV 사용 : 목록을 정렬 된 순서로 가져 오기가 어렵습니다.

XML로 : 특별히 어렵지 않은


CSV 사용 : 정수를 문자열로 저장하면 이진 정수를 저장하는 것보다 두 배의 공간이 필요합니다.

XML을 사용하면 스토리지가 CSV보다 더 나빠집니다.


CSV 사용 : 많은 쉼표 문자.

XML 사용 : 쉼표 대신 태그가 사용됨


요컨대, XML을 사용하면 구분 된 목록의 일부 문제가 발생하고 필요에 따라 구분 된 목록으로 변환 할 수 있습니다


6

예, 그것은 이다 그 나쁜. 제 생각에는 관계형 데이터베이스를 사용하는 것이 마음에 들지 않으면 자신에게 더 적합한 대안을 찾으면 정말 고급 기능을 갖춘 흥미로운 "NOSQL"프로젝트가 많이 있다는 것입니다.


0

아마도 중간 단계를 취할 것입니다 .CSV의 각 필드를 데이터베이스의 별도 열로 만들지 만 정규화에 대해서는 크게 걱정하지 마십시오 (적어도 현재). 언젠가는 정규화 가 흥미로워 질 있지만 모든 데이터를 단일 열에 넣은 상태에서 데이터베이스를 사용하면 거의 이점이 없습니다. 의미있는 데이터를 조작하기 전에 데이터를 논리 필드 / 열 / 전화하려는 항목으로 분리해야합니다.


이 양식에는 더 많은 필드가 포함되어 있습니다.이 양식의 한 부분 일뿐입니다 (질문에서 잘 설명하지 못했습니다).
미친 과학자

0

고정 된 개수의 부울 필드가있는 경우 각 필드에 INT(1) NOT NULL(또는 BIT NOT NULL존재하는 경우) 또는 CHAR (0)(널 가능)을 사용할 수 있습니다. SET(정확한 구문을 잊어 버렸습니다)를 사용할 수도 있습니다 .


1
INT(1)4 바이트가 걸립니다. 는 (1)의미가 없다.
Rick James
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.