Việc lưu trữ một danh sách giới hạn trong một cột cơ sở dữ liệu có thực sự xấu không?


363

Hãy tưởng tượng một hình thức web với một bộ hộp kiểm tra (bất kỳ hoặc tất cả chúng có thể được chọn). Tôi đã chọn lưu chúng trong một danh sách các giá trị được phân tách bằng dấu phẩy được lưu trữ trong một cột của bảng cơ sở dữ liệu.

Bây giờ, tôi biết rằng giải pháp chính xác sẽ là tạo một bảng thứ hai và chuẩn hóa đúng cơ sở dữ liệu. Thật nhanh chóng để thực hiện giải pháp dễ dàng và tôi muốn có một bằng chứng về khái niệm ứng dụng đó một cách nhanh chóng và không phải mất quá nhiều thời gian cho nó.

Tôi nghĩ rằng thời gian tiết kiệm và mã đơn giản hơn đáng giá trong tình huống của tôi, đây có phải là một lựa chọn thiết kế có thể phòng thủ được không, hay tôi nên bình thường hóa nó ngay từ đầu?

Một số bối cảnh khác, đây là một ứng dụng nội bộ nhỏ về cơ bản thay thế một tệp Excel được lưu trữ trên một thư mục dùng chung. Tôi cũng đang hỏi bởi vì tôi đang nghĩ về việc làm sạch chương trình và làm cho nó dễ bảo trì hơn. Có một số điều trong đó tôi không hoàn toàn hài lòng, một trong số đó là chủ đề của câu hỏi này.


21
trong trường hợp đó, tại sao phải làm phiền cơ sở dữ liệu?, lưu trong một tệp sẽ làm gì.
thavan

6
Đồng ý với @thavan. Tại sao thậm chí lưu dữ liệu cho một bằng chứng về khái niệm? Khi bạn đã có bằng chứng đầy đủ, sau đó thêm một cơ sở dữ liệu chính xác. Việc bạn làm nhẹ để chứng minh khái niệm, đừng làm những điều bạn phải làm sau này.
Jeff Davis

1
Trong Postgres, một cột mảng nên được ưu tiên hơn một danh sách được phân tách bằng dấu phẩy. Điều đó ít nhất đảm bảo loại dữ liệu phù hợp, không có vấn đề gì trong việc phân biệt dấu phân cách với dữ liệu thực tế và nó có thể được lập chỉ mục một cách hiệu quả.
a_horse_with_no_name

Câu trả lời:


568

Ngoài việc vi phạm Biểu mẫu thông thường đầu tiên do nhóm các giá trị lặp lại được lưu trữ trong một cột duy nhất, danh sách được phân tách bằng dấu phẩy còn có nhiều vấn đề thực tế khác:

  • Không thể đảm bảo rằng mỗi giá trị là loại dữ liệu phù hợp: không có cách nào để ngăn 1,2,3, chuối, 5
  • Không thể sử dụng các ràng buộc khóa ngoài để liên kết các giá trị với bảng tra cứu; không có cách nào để thực thi toàn vẹn tham chiếu.
  • Không thể thực thi tính duy nhất: không có cách nào để ngăn chặn 1,2,3,3,3,5
  • Không thể xóa một giá trị khỏi danh sách mà không tìm nạp toàn bộ danh sách.
  • Không thể lưu trữ một danh sách dài hơn những gì phù hợp trong cột chuỗi.
  • Khó tìm kiếm tất cả các thực thể với một giá trị nhất định trong danh sách; bạn phải sử dụng quét bảng không hiệu quả. Có thể phải dùng đến các biểu thức chính quy, ví dụ như trong MySQL:
    idlist REGEXP '[[:<:]]2[[:>:]]'*
  • Khó đếm các yếu tố trong danh sách, hoặc thực hiện các truy vấn tổng hợp khác.
  • Khó tham gia các giá trị vào bảng tra cứu mà họ tham chiếu.
  • Khó để lấy danh sách theo thứ tự sắp xếp.

Để giải quyết những vấn đề này, bạn phải viết hàng tấn mã ứng dụng, phát minh lại chức năng mà RDBMS đã cung cấp hiệu quả hơn nhiều .

Các danh sách được phân tách bằng dấu phẩy đủ sai để tôi tạo ra chương đầu tiên trong cuốn sách của mình: SQL Antipotypes: Tránh những cạm bẫy của lập trình cơ sở dữ liệu .

Có những lúc bạn cần sử dụng sự không chuẩn hóa, nhưng như @OMG Ponies đề cập , đây là những trường hợp ngoại lệ. Bất kỳ tối ưu hóa nào không liên quan đến mối quan hệ trực tuyến, đều có lợi cho một loại truy vấn với chi phí sử dụng dữ liệu khác, vì vậy hãy chắc chắn rằng bạn biết những truy vấn nào cần được xử lý đặc biệt để chúng đáng được chuẩn hóa.


* MySQL 8.0 không còn hỗ trợ cú pháp biểu thức ranh giới từ này.


8
Một ARRAY (của bất kỳ kiểu dữ liệu nào) có thể khắc phục ngoại lệ, chỉ cần kiểm tra PostgreSQL: postgresql.org/docs/cien/static/arrays.html (@Bill: Cuốn sách tuyệt vời, phải đọc cho bất kỳ nhà phát triển hoặc dba nào)
Frank Heikens

4
Hóa đơn +1 Karwin Câu trả lời tuyệt vời! Điểm đạn ngắn gọn đáng yêu. Trông giống như một cuốn sách tuyệt vời quá. Yêu bìa quá +1 NullUserException. Tôi đang trong quá trình thiết kế lược đồ cho cơ sở dữ liệu MySQL để thay thế một hệ thống dựa trên văn bản tệp phẳng. Tôi đã gặp một số tình huống khó xử cho đến nay. Vì vậy, cuốn sách này sẽ có giá trị mua.
trị liệu

2
Trang web pragprog.com cũng có vẻ tốt: kiểu dáng đẹp, bố cục, thân thiện với người dùng. Điều này phải khá mới, tôi đã không thể mua sách điện tử của họ trong quá khứ. Tái bút Tôi không làm việc cho họ có bất kỳ kết nối với các tác giả. Tôi thích ăn mừng những sản phẩm, dịch vụ tốt và sự giúp đỡ khi tôi nhìn thấy nó.
trị liệu

2
Về mặt nghiêm trọng, tôi sẽ thêm vào danh sách của bạn: Khó tìm kiếm. Giả sử bạn muốn tất cả các bản ghi bao gồm "2". Tất nhiên, bạn không thể chỉ tìm kiếm foobar = '2' vì điều đó sẽ bỏ lỡ nếu có các giá trị khác. Bạn không thể tìm kiếm foobar như '% 2%' vì điều đó sẽ nhận được lượt truy cập sai trong 12 và 28, v.v. Bạn không thể tìm kiếm foobar như '%, 2,%' vì 2 có thể là thành phần đầu tiên hoặc cuối cùng của danh sách và do đó chỉ có một trong những dấu phẩy đó.
Jay

2
Tôi biết điều đó không được khuyến khích, nhưng chơi quỷ ủng hộ: hầu hết những thứ này có thể được gỡ bỏ nếu có một ui xử lý các kiểu dữ liệu và tính duy nhất (nếu không sẽ có lỗi hoặc hoạt động sai), dù sao cũng có một bảng điều khiển các giá trị đến từ để làm cho chúng trở nên độc nhất, trường như '% P%' có thể được sử dụng, các giá trị là P, R, S, T, không quan trọng và việc sắp xếp không thành vấn đề. Tùy thuộc vào ui, các giá trị có thể được phân tách [] ví dụ: để kiểm tra các hộp kiểm trong danh sách từ bảng trình điều khiển trong kịch bản ít phổ biến nhất mà không phải chuyển sang bảng khác để lấy chúng.
jmcclure

44

"Một lý do là sự lười biếng".

Đây là hồi chuông cảnh báo. Lý do duy nhất bạn nên làm một cái gì đó như thế này là bạn biết cách làm "đúng cách" nhưng bạn đã đi đến kết luận rằng có một lý do hữu hình để không làm theo cách đó.

Đã nói điều này: nếu dữ liệu bạn chọn để lưu trữ theo cách này là dữ liệu mà bạn sẽ không bao giờ cần truy vấn, thì có thể có trường hợp lưu trữ nó theo cách bạn đã chọn.

(Một số người dùng sẽ tranh chấp tuyên bố trong đoạn trước của tôi, nói rằng "bạn không bao giờ có thể biết những yêu cầu nào sẽ được thêm vào trong tương lai". có trước bạn.)


Tôi luôn nghe một số người nói rằng "thiết kế của tôi linh hoạt hơn của bạn" khi tôi đối mặt với họ về những điều như không thiết lập các ràng buộc khóa ngoại hoặc lưu trữ danh sách trong một trường duy nhất. Đối với tôi, tính linh hoạt (trong những trường hợp như vậy) == không có kỷ luật == sự lười biếng.
tầm nhìn xa

41

Có rất nhiều câu hỏi về SO hỏi:

  • làm cách nào để có được số lượng giá trị cụ thể từ danh sách được phân tách bằng dấu phẩy
  • làm thế nào để có được các bản ghi chỉ có cùng giá trị cụ thể 2/3 / etc từ danh sách được phân tách bằng dấu phẩy đó

Một vấn đề khác với danh sách được phân tách bằng dấu phẩy là đảm bảo các giá trị nhất quán - lưu trữ văn bản có nghĩa là khả năng lỗi chính tả ...

Đây là tất cả các triệu chứng của dữ liệu không chuẩn hóa và nêu bật lý do tại sao bạn phải luôn lập mô hình cho dữ liệu được chuẩn hóa. Không chuẩn hóa có thể là một tối ưu hóa truy vấn, được áp dụng khi nhu cầu thực sự xuất hiện .


19

Nói chung, mọi thứ đều có thể phòng thủ được nếu nó đáp ứng các yêu cầu của dự án của bạn. Điều này không có nghĩa là mọi người sẽ đồng ý hoặc muốn bảo vệ quyết định của bạn ...

Nói chung, việc lưu trữ dữ liệu theo cách này là tối ưu (ví dụ: khó thực hiện các truy vấn hiệu quả hơn) và có thể gây ra sự cố bảo trì nếu bạn sửa đổi các mục trong biểu mẫu của mình. Có lẽ bạn có thể đã tìm thấy một nền tảng ở giữa và sử dụng một số nguyên đại diện cho một tập hợp các cờ bit thay thế?


10

Vâng, tôi sẽ nói rằng nó thực sự là xấu. Đó là một lựa chọn phòng thủ, nhưng điều đó không làm cho nó đúng hay tốt.

Nó phá vỡ hình thức bình thường đầu tiên.

Một chỉ trích thứ hai là việc đưa các kết quả đầu vào thô trực tiếp vào cơ sở dữ liệu, mà không có bất kỳ xác nhận hay ràng buộc nào, khiến bạn mở các cuộc tấn công SQL SQL.

Những gì bạn đang gọi là sự lười biếng và thiếu kiến ​​thức SQL là những thứ mà neophyte được tạo ra. Tôi khuyên bạn nên dành thời gian để làm điều đó đúng cách và xem nó như một cơ hội để học hỏi.

Hoặc để nguyên như vậy và học bài học đau đớn về một cuộc tấn công tiêm nhiễm SQL.


19
Tôi không thấy bất cứ điều gì trong câu hỏi này cho thấy anh ta dễ bị tổn thương khi tiêm SQL. Việc tiêm SQL và chuẩn hóa cơ sở dữ liệu là các chủ đề trực giao và sự lạc quan của bạn về việc tiêm không liên quan đến câu hỏi.
Hammerite

5
@Paul: Và có thể thái độ tương tự sẽ dẫn đến việc anh ta bị xe buýt đâm khi anh ta không nhìn cả hai chiều trước khi băng qua đường, nhưng bạn đã không cảnh báo anh ta về điều đó. Chỉnh sửa: Tôi đã nghĩ rằng bạn là người đăng câu trả lời này, lỗi của tôi.
Hammerite

1
@ Hammermerite - phép ngoại suy của bạn đối với xe buýt là vô lý.
duffymo

4
Vâng, nó đã được dự định là vô lý. Sự lố bịch của nó minh họa cho quan điểm mà tôi đang đưa ra, đó là việc cảnh báo anh ta chống lại điều gì đó mà bạn không có lý do gì để nghĩ rằng anh ta cần phải được cảnh báo.
Hammerite

1
Có, tôi thấy. Tôi nghĩ rằng tôi đã có nhiều lý do mà cảnh báo của bạn về xe buýt.
duffymo

7

Vâng, tôi đã sử dụng danh sách tách cặp khóa / giá trị trong một cột NTEXT trong SQL Server hơn 4 năm nay và nó hoạt động. Bạn làm mất tính linh hoạt của việc thực hiện các truy vấn nhưng mặt khác, nếu bạn có một thư viện vẫn tồn tại / tạo ra cặp giá trị chính thì đó không phải là một ý tưởng tồi.


13
Không, đó là một ý tưởng khủng khiếp. Bạn đã xoay sở để thoát khỏi nó, nhưng chi phí cho vài phút thời gian phát triển của bạn đã khiến bạn tốn kém về hiệu năng truy vấn, tính linh hoạt và khả năng duy trì mã của bạn.
Paul Tomblin

5
Paul, tôi đồng ý. Nhưng như tôi đã nói, tôi đã sử dụng nếu cho một mục đích cụ thể và đó là cho một hoạt động nhập dữ liệu nơi bạn có nhiều loại biểu mẫu. Bây giờ tôi đang sửa đổi thiết kế mà tôi đã học NHibernate nhưng trước đó tôi cần sự linh hoạt để thiết kế biểu mẫu trong ASP.NET và sử dụng id hộp văn bản làm khóa trong cặp khóa / giá trị.
Raj

28
+1 chỉ để chống lại các downvote. Nói với ai đó đã duy trì ứng dụng trong 4 năm về các mối quan tâm bảo trì là một chút tự phụ. Có rất ít ý tưởng "khủng khiếp" trong phát triển sw - chủ yếu chỉ là những ý tưởng với khả năng ứng dụng rất hạn chế. Thật hợp lý khi cảnh báo mọi người về những hạn chế, nhưng việc trừng phạt những người đã thực hiện nó và sống qua đó đánh vào tôi như một thái độ thánh thiện hơn tôi có thể làm mà không cần.
Mark Brackett

7

Tôi cần một cột đa giá trị, nó có thể được triển khai dưới dạng trường xml

Nó có thể được chuyển đổi thành dấu phẩy được phân cách khi cần thiết

truy vấn danh sách XML trong máy chủ sql bằng Xquery .

Bằng cách là một trường xml, một số mối quan tâm có thể được giải quyết.

Với CSV: Không thể đảm bảo rằng mỗi giá trị là loại dữ liệu phù hợp: không có cách nào để ngăn 1,2,3, chuối, 5

Với XML: các giá trị trong thẻ có thể bị buộc phải là loại chính xác


Với CSV: Không thể sử dụng các ràng buộc khóa ngoài để liên kết các giá trị với bảng tra cứu; không có cách nào để thực thi toàn vẹn tham chiếu.

Với XML: vẫn là một vấn đề


Với CSV: Không thể thực thi tính duy nhất: không có cách nào để ngăn chặn 1,2,3,3,3,5

Với XML: vẫn là một vấn đề


Với CSV: Không thể xóa một giá trị khỏi danh sách mà không tìm nạp toàn bộ danh sách.

Với XML: các mục đơn lẻ có thể được xóa


Với CSV: Khó tìm kiếm tất cả các thực thể có giá trị nhất định trong danh sách; bạn phải sử dụng quét bảng không hiệu quả.

Với XML: trường xml có thể được lập chỉ mục


Với CSV: Khó đếm các yếu tố trong danh sách hoặc thực hiện các truy vấn tổng hợp khác. **

Với XML: không đặc biệt khó


Với CSV: Khó tham gia các giá trị vào bảng tra cứu mà họ tham chiếu. **

Với XML: không đặc biệt khó


Với CSV: Khó tìm nạp danh sách theo thứ tự được sắp xếp.

Với XML: không đặc biệt khó


Với CSV: Lưu trữ số nguyên dưới dạng chuỗi chiếm khoảng gấp đôi dung lượng so với lưu trữ số nguyên nhị phân.

Với XML: lưu trữ thậm chí còn tồi tệ hơn một csv


Với CSV: Cộng với rất nhiều ký tự dấu phẩy.

Với XML: thẻ được sử dụng thay vì dấu phẩy


Nói tóm lại, việc sử dụng XML sẽ giải quyết một số vấn đề với danh sách được phân tách VÀ có thể được chuyển đổi thành danh sách được phân tách khi cần thiết


6

Vâng, đó xấu. Quan điểm của tôi là nếu bạn không thích sử dụng cơ sở dữ liệu quan hệ thì hãy tìm một giải pháp thay thế phù hợp với bạn hơn, có rất nhiều dự án "NOSQL" thú vị ngoài kia với một số tính năng thực sự tiên tiến.


0

Tôi có lẽ sẽ lấy điểm giữa: biến mỗi trường trong CSV thành một cột riêng trong cơ sở dữ liệu, nhưng không phải lo lắng nhiều về việc chuẩn hóa (ít nhất là bây giờ). Tại một số điểm, việc chuẩn hóa có thể trở nên thú vị, nhưng với tất cả dữ liệu được đưa vào một cột duy nhất, bạn sẽ hầu như không có lợi ích gì khi sử dụng cơ sở dữ liệu. Bạn cần tách dữ liệu thành các trường / cột logic / bất cứ điều gì bạn muốn gọi chúng trước khi bạn có thể thao tác dữ liệu một cách có ý nghĩa.


Biểu mẫu chứa một số trường khác, đây chỉ là một phần của biểu mẫu (mà tôi không giải thích rõ trong câu hỏi).
Nhà khoa học điên

0

Nếu bạn có một số trường boolean cố định, bạn có thể sử dụng một INT(1) NOT NULL(hoặc BIT NOT NULLnếu nó tồn tại) hoặc CHAR (0)(nullable) cho mỗi trường. Bạn cũng có thể sử dụng một SET(tôi quên cú pháp chính xác).


1
INT(1)mất 4 byte; Điều (1)này là vô nghĩa.
Rick James
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.