Có phải thực tế xấu khi cho phép người dùng xác định các trường?


17

Nói chung, nó có được coi là thông lệ xấu khi cho phép người dùng tạo các trường trong cơ sở dữ liệu cho ứng dụng web không?

Ví dụ, tôi đang làm một ứng dụng web kiểm kê nhà cho vợ tôi và cô ấy sẽ muốn xác định các lĩnh vực của riêng mình cho các mặt hàng khác nhau. Tôi đã lên kế hoạch cho phép cô ấy tạo ra các danh mục và thêm "tính năng" vào các danh mục đó. Các tính năng sẽ chỉ là khóa / giá trị được lưu trữ dưới dạng chuỗi. Bằng cách đó, nếu cô ấy có một danh mục gọi là "Audio CD", cô ấy có thể thêm các tính năng cho nội dung như "nghệ sĩ", "bài hát", v.v. Nhưng trong một danh mục khác như "nội thất", cô ấy có thể thêm các tính năng cho nội dung như "vật liệu" "(Gỗ, nhựa, v.v.). Sau đó, bất kỳ mục nào có thể thuộc về một (hoặc nhiều) danh mục, thêm các tính năng đó vào mục.

Tôi có thể thấy các vấn đề trong đó việc tìm kiếm bằng các tính năng này yêu cầu so sánh chuỗi, không có xác thực dữ liệu, v.v. Theo phương pháp nhanh, có lẽ sẽ tốt hơn nếu cô ấy đưa ra các danh mục và thuộc tính mới và tôi sẽ phải tạo các bảng mới khi chúng ta đi Trong ví dụ của tôi, đó là một cơ sở người dùng nhỏ (2 người chúng tôi) và số lượng hồ sơ được tạo sẽ nhỏ, vì vậy không quá tệ.

Nói chung, mặc dù, làm thế nào để mọi người xử lý một cái gì đó như thế này trong "cuộc sống thực"?


4
Bạn đã cân nhắc sử dụng cơ sở dữ liệu hướng tài liệu như MongoDB chưa? Bạn có thể lưu trữ một tài liệu cho mỗi loại hoạt động như một lược đồ cũng có thể được chỉnh sửa (có thể là thủ công, với quy mô nhỏ của dự án).
Andy Hunt

@AndyBursh một trong những bit 'vui nhộn' với các postgres hiện tại là kiểu dữ liệu ( liên kết ) 'json' . Cách tiếp cận như vậy sẽ cho phép một người lưu trữ các trường do người dùng chỉ định trong dữ liệu đó, er, document, er, bất cứ điều gì và sau đó sử dụng phần còn lại của các trường cho những thứ mà bạn lập chỉ mục phù hợp và tương tự. Mặc dù tất cả điều này phụ thuộc vào cách sử dụng và thật khó để nói liệu điều này có hoạt động tốt cho một ứng dụng cụ thể hay không. Nhưng nó là một cái gì đó để nhận thức được.

tất cả: thảo luận tuyệt vời, cảm ơn vì tất cả những hiểu biết! @AndyBursh Tôi đã nghe nói về MongoDB nhưng chưa bao giờ thực sự đọc về nó. Âm thanh giống như một dự án nhà khác để thử nghiệm với ...
zako42

Câu trả lời:


19

Khi bạn bắt đầu nhận được "các trường do người dùng xác định" như thường thấy trong trình theo dõi lỗi, quản lý tài nguyên khách hàng và các công cụ kinh doanh tương tự là chúng không được hỗ trợ với một bảng có các trường bajillion (nếu có, thì đó có thể là vấn đề của của riêng nó).

Thay vào đó, những gì bạn tìm thấy là các thiết kế bảng Giá trị thuộc tính thực thể và công cụ quản trị được liên kết để quản lý các thuộc tính hợp lệ.

Hãy xem xét bảng sau:

  + -------------- +
  | điều |
  | -------------- |
  | id |
  | loại |
  | giải thích |
  | attr1 |
  | attr2 |
  | attr3 |
  | attr4 |
  | attr5 |
  + -------------- +

Đây là sau khi bạn đã thêm một vài thuộc tính. Thay vì attr1giả vờ nó đọc artisthoặc trackshoặc genrehoặc bất cứ điều gì thuộc tính điều có. Và thay vì 5, nếu nó là 50. Rõ ràng là không thể quản lý được. Nó cũng yêu cầu cập nhật mô hình và triển khai lại ứng dụng để xử lý một lĩnh vực mới. Không lý tưởng.

Bây giờ hãy xem xét cấu trúc bảng sau:

  + -------------- + + --------------- + + ------------- +
  | điều | | điều_attr | | attr |
  | -------------- | | --------------- | | ------------- |
  | id | <--- + | điều_id (fk) | +> | id |
  | loại | | attr_id (fk) | + - + | tên |
  | giải thích | | giá trị | | |
  + -------------- + + --------------- + + ------------- +

Bạn đã có điều của bạn với các lĩnh vực cơ bản của nó. Bạn có thêm hai bảng. Một với các thuộc tính. Mỗi lĩnh vực là một hàng trong attrbảng. Và sau đó là thing_attrmột cặp khóa ngoại liên quan đến thingbảng và attrbảng. Và điều này sau đó có một trường giá trị nơi bạn lưu trữ bất cứ giá trị nào của trường cho thực thể đó.

Và bây giờ bạn đã có một cấu trúc trong đó bảng attr có thể được cập nhật trong thời gian chạy và các trường mới có thể được thêm (hoặc xóa) một cách nhanh chóng mà không ảnh hưởng đáng kể đến ứng dụng tổng thể.

Các truy vấn phức tạp hơn một chút và xác thực cũng trở nên phức tạp hơn (cả thủ tục lưu trữ sôi nổi hoặc tất cả các phía máy khách). Đó là một sự đánh đổi trong thiết kế.

Cũng xem xét tình huống một ngày nào đó bạn cần thực hiện di chuyển và bạn quay lại ứng dụng để thấy rằng hiện tại có hơn một nửa tá hoặc nhiều hơn các thuộc tính so với lược đồ bạn đã phân phối ban đầu. Điều này làm cho việc di chuyển và nâng cấp xấu trong đó bảng Giá trị thuộc tính thực thể, khi được sử dụng đúng cách, có thể sạch hơn. (Không phải luôn luôn, nhưng có thể.)


Có bất kỳ nhược điểm nào khi chỉ sửa đổi lược đồ khi chạy không? Nếu người dùng nghĩ rằng một thứ cần một thuộc tính mới, chỉ cần tự động thêm một cột vào bảng?

Nếu bạn đang làm việc với hương vị thích hợp của cơ sở dữ liệu nosql, có lẽ bạn có thể làm điều này (lưu ý rằng hương vị thích hợp của nosql cho điều này có thể sẽ là một kho lưu trữ khóa-giá trị , đó là bảng EAV cho các mối quan hệ được mô tả ở trên) không có quá nhiều rắc rối Tuy nhiên, nó đi kèm với tất cả các thỏa hiệp cho nosql được mô tả ở nơi khác rất chi tiết.

Nếu bạn đang làm việc trên một cơ sở dữ liệu quan hệ - bạn cần phải có lược đồ. Thêm cột một cách linh hoạt có nghĩa là một số tập hợp con của những điều sau đây là đúng:

  • Bạn đang làm lập trình siêu cơ sở dữ liệu. Thay vì có thể ánh xạ sạch cột này sang trường đó bằng một ORM đẹp mắt, có lẽ bạn đang làm những việc như thế select *và sau đó thực hiện một số mã phức tạp để tìm hiểu dữ liệu thực sự là gì (xem Kết quả của Java SetMetaData ) và sau đó lưu trữ nó trong bản đồ ( hoặc một số kiểu dữ liệu khác - nhưng các trường không đẹp trong mã). Điều này sau đó sẽ loại bỏ một chút an toàn về loại và lỗi đánh máy mà bạn có với cách tiếp cận truyền thống.
  • Bạn có thể đã từ bỏ ORM. Điều này có nghĩa là bạn đang viết sql thô cho tất cả các mã thay vì để hệ thống thực hiện công việc cho bạn.
  • Bạn đã từ bỏ việc nâng cấp sạch sẽ. Điều gì xảy ra khi khách hàng thêm một trường có một tên mà phiên bản tiếp theo của bạn cũng sử dụng? Trong trang web mai mối, bản nâng cấp muốn thêm hasdatetrường để lưu dấu thời gian đã được xác định như hasdatevới boolean cho một trận đấu thành công ... và việc nâng cấp của bạn bị phá vỡ.
  • Bạn tin tưởng rằng khách hàng không phá vỡ hệ thống bằng cách sử dụng một số từ dành riêng cũng phá vỡ các truy vấn của bạn ... ở đâu đó.
  • Bạn đã ràng buộc mình với một thương hiệu cơ sở dữ liệu. Các DDL của cơ sở dữ liệu khác nhau là khác nhau. Các loại cơ sở dữ liệu là ví dụ dễ nhất về điều này. varchar2vs textvà tương tự. Mã của bạn để thêm cột sẽ hoạt động trên MySQL nhưng không phải Postgres hoặc Oracle hoặc SQL Server.
  • Bạn có tin tưởng khách hàng để thực sự thêm dữ liệu tốt ? Chắc chắn, EAV không còn lý tưởng nhưng bây giờ bạn đã có một số tên bảng tối nghĩa khủng khiếp mà nhà phát triển không thêm, với loại chỉ mục sai (nếu có), không có ràng buộc nào được thêm vào mã khi cần được và như vậy.
  • Bạn đã trao đặc quyền sửa đổi lược đồ cho người dùng đang chạy ứng dụng. Các bảng thả nhỏ Bobby không thể thực hiện được khi bạn bị hạn chế đối với SQL thay vì DDL (chắc chắn bạn có thể thực hiện delete * from studentsthay thế, nhưng bạn thực sự không thể làm xáo trộn cơ sở dữ liệu theo những cách xấu). Số lượng điều có thể sai với truy cập lược đồ hoặc từ một vụ tai nạn hoặc hoạt động độc hại trên bầu trời.

Điều này thực sự sôi sục đến "đừng làm điều đó." Nếu bạn thực sự muốn điều này, hãy đi với một mẫu đã biết của cấu trúc bảng EAV hoặc cơ sở dữ liệu hoàn toàn dành riêng cho cấu trúc này. Đừng để mọi người tạo các trường tùy ý trong một bảng. Những cơn đau đầu chỉ là không đáng.


4
Bạn cũng đã phát minh lại cơ sở dữ liệu.
dùng253751

1
@immibis nó đã thêm một lớp trong đó người dùng có thể quản trị mà không cần xáo trộn phần còn lại của cơ sở dữ liệu hoặc yêu cầu triển khai lại để cập nhật mô hình.

1
@immibis EAV đã tranh luận sôi nổi trong giới cơ sở dữ liệu quan hệ trong nhiều năm. Về lý thuyết, điều đó là không cần thiết, nhưng trong thực tế, bạn không thể làm một số việc mà không có nó.
Ross Patterson

1
@ShivanDragon đi đến cách tiếp cận NoQuery. Kho tài liệu chỉ lưu trữ tài liệu và không áp đặt lược đồ. Như vậy, việc thêm và xóa các trường và phân tích tài liệu hoàn toàn nằm ngoài phạm vi của chính cơ sở dữ liệu (và bạn đã viết mô hình của mình để phù hợp với điều đó). Đó là một tập hợp thỏa hiệp hoàn toàn khác so với thỏa hiệp cơ sở dữ liệu quan hệ cho cấu trúc EAV.


5

Làm tốt điều này thật khó.

Đối với một ứng dụng một lần như những gì bạn đang lập kế hoạch, tất nhiên, bạn có thể chỉ cần thêm một cột cho mỗi trường và cung cấp giao diện người dùng giúp định nghĩa trường bởi người dùng chưa được huấn luyện an toàn hơn là cung cấp cho họ một dòng lệnh SQL. Hoặc bạn có thể theo mô hình Entity-Attribution-Value đáng sợ , đây là một phản ứng kinh điển, nếu hơi đáng sợ, đối với loại vấn đề này. Xây dựng giao diện người dùng để xác định các trường EAV thường phức tạp hơn nhiều so với các cột cơ sở dữ liệu và các truy vấn có thể có nhiều lông, nhưng đối với số lượng lớn các trường ( ví dụ , lược đồ ma trận rất thưa thớt), đó có thể là cách duy nhất để có được công việc đã hoàn thành


Tóm lại: dự án nhỏ == KISS. Nhanh nhẹn xuống đất.
Encaitar

Vấn đề với cập nhật bảng cơ sở dữ liệu, là tùy thuộc vào lượng dữ liệu và chỉ mục cần thiết (các trường tùy chỉnh thường yêu cầu các tính năng tìm kiếm), truy vấn thay đổi bảng có thể mất một lượng thời gian khổng lồ. Câu chuyện dài là MySQL và các cơ sở dữ liệu quan hệ khác đơn giản không phải là phương tiện tốt cho loại yêu cầu này.
Oddman

0

Tôi đã vượt qua một số điều tương tự gần đây.

Tôi làm 2 bàn.

1: table Objects 
    Id , name, type

Anh ấy là tất cả các đối tượng của bạn. U đặt tên của nó.

Và một loại đối tượng này: - đối với tôi các loại có sẵn là hàng tồn kho, Invent_item, office.

Và thiết lập thông thường là n mục là con hoặc hàng tồn kho cũng là con của văn phòng và tôi đã sử dụng một bảng tham gia để nối các đối tượng với nhau

2 table settings 
     organization_Id , title, value , type

Bảng cài đặt chứa mọi tên trường cho loại đối tượng cụ thể và giá trị trong giá trị.

Ví dụ tính chất của văn phòng

Địa điểm, điện thoại, giờ làm việc

Và cho các mặt hàng

  • Số tiền
  • Giá bán
  • Mã vạch

Vv, tất cả các thuộc tính này được mô hình hóa của bạn thi hành và được lưu trong bảng cài đặt dưới dạng các hàng riêng biệt (chưa sử dụng thay thế không chèn để tránh nhiều hàng cho cùng một trường)

Vì vậy, bất cứ khi nào tôi muốn có một văn phòng, tôi tải nó dễ dàng với tất cả các quan hệ và cài đặt của nó trong đó cài đặt object_I'd (đối tượng được yêu cầu)

Sau đó, tôi xoay tất cả các hàng từ cài đặt và đó là nó.

Và trong trường hợp tôi muốn một cài đặt dành riêng cho một mục trong kho (không phải toàn cầu), tôi đặt object_I'd = Tôi từ bảng quan hệ object_objects và tôi đặt settings.type = perspective_setting

Tôi hy vọng bạn hiểu ý tôi muốn nói, tôi sẽ cố gắng định dạng lại câu trả lời khi tôi đến máy tính xách tay


2
Mẹo chuyên nghiệp - không đăng bài trên diễn đàn này từ điện thoại của bạn. Tự động sửa làm cho các phần của bài viết của bạn không thể đọc được.
BobDalgleish

Haha quan sát tốt :)
Zalaboza

0

Có phải thực tế xấu khi cho phép người dùng xác định các trường?

Không, nó không phải là thực hành xấu. Nó khá là phổ biến. Trong thuật ngữ OO, điều này được gọi là thừa kế. Bạn có một kho lưu trữ lớp cơ sở và hai lớp kế thừa AudioCD và đồ nội thất.

Nói chung, mặc dù, làm thế nào để mọi người xử lý một cái gì đó như thế này trong "cuộc sống thực"?

Bạn phải quyết định cách InventItem, AudioCD và đồ nội thất được lưu trữ trong cơ sở dữ liệu.

Nếu truy vấn dễ là quan trọng nhất đối với bạn và db-space / chuẩn hóa không thành vấn đề thì bạn sẽ thực hiện lược đồ "bảng trên mỗi phân cấp".

Nếu không gian / chuẩn hóa là quan trọng nhất đối với bạn và các truy vấn phức tạp hơn không có vấn đề gì với bạn thì bạn sẽ thực hiện Lược đồ "bảng mỗi loại".

Để biết thêm chi tiết, hãy xem kế thừa bảng dot-per-type-vs-table-per-hVELy-thừa kế hoặc java hibernate .


Tôi không biết nếu điều này giải quyết câu hỏi. Người dùng không sửa đổi mã để tạo các lớp mới
Colin D
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.