Khi nào / Tại sao nên sử dụng Cascading trong SQL Server?


149

Khi thiết lập các khóa ngoại trong SQL Server, trong trường hợp nào bạn nên sử dụng khóa đó để xóa hoặc cập nhật, và lý do đằng sau nó là gì?

Điều này có thể áp dụng cho các cơ sở dữ liệu khác là tốt.

Tôi đang tìm kiếm hầu hết tất cả các ví dụ cụ thể cho từng kịch bản, tốt nhất là từ một người đã sử dụng chúng thành công.


4
Câu hỏi này dường như không liên quan chặt chẽ đến SQL Server và trông giống như một câu hỏi chung về mặt lý thuyết. Sẽ hữu ích hơn cho cộng đồng nếu bạn xóa sql-serverthẻ.
clapas

4
@clapas Thành thật mà nói, nếu tôi hỏi nó hôm nay thì nó sẽ lạc đề. Nếu không có lượt xem / lượt bình chọn cao cho thấy nó có giá trị với cộng đồng thì tôi chỉ cần xóa nó.
Joel Coehoorn

6
@JoelCoehoorn - Rõ ràng những loại câu hỏi này có giá trị. Giá trị này không tiêu tan do thời gian trôi qua. Câu hỏi trong đầu tôi là chúng ta mất bao nhiêu giá trị bằng cách không cho phép những câu hỏi như vậy ngày hôm nay?
P.Brian.Mackey

2
@ P.Brian.Mackey Đây, Đây! Một số câu hỏi / câu trả lời hay nhất tôi từng thấy là những câu hỏi đã bị bỏ phiếu hoặc bị coi là lạc đề ... nhưng bạn có thể nhận ra số lượng phiếu bầu LỚN mà nhiều người có cùng một câu hỏi!
Anthony Griggs

Hành động xếp chồng mất khóa tuần tự.
Mitch Wheat

Câu trả lời:


126

Tóm tắt những gì tôi đã thấy cho đến nay:

  • Một số người không thích xếp tầng.

Xóa tầng

  • Cascade Delete có thể có ý nghĩa khi ngữ nghĩa của mối quan hệ có thể liên quan đến một mô tả "là một phần" độc quyền . Ví dụ: bản ghi OrderLine là một phần của đơn hàng mẹ và OrderLines sẽ không bao giờ được chia sẻ giữa nhiều đơn hàng. Nếu Đơn hàng biến mất, OrderLine cũng vậy, và một dòng không có Đơn hàng sẽ là một vấn đề.
  • Ví dụ chính tắc cho Cascade Delete là someObject và someObjectItems, trong đó nó không có ý nghĩa gì đối với một bản ghi vật phẩm tồn tại mà không có bản ghi chính tương ứng.
  • Bạn không nên sử dụng Cascade Delete nếu bạn đang lưu giữ lịch sử hoặc sử dụng "xóa mềm / logic" khi bạn chỉ đặt cột bit bị xóa thành 1 / true.

Cập nhật tầng

  • Cập nhật Cascade có thể có ý nghĩa khi bạn sử dụng khóa thực chứ không phải là khóa thay thế (cột nhận dạng / tự động) trên các bảng.
  • Ví dụ điển hình cho Cập nhật Cascade là khi bạn có khóa ngoại có thể thay đổi, giống như tên người dùng có thể thay đổi.
  • Bạn không nên sử dụng Cascade Update với các khóa là các cột Nhận dạng / tự động.
  • Cập nhật Cascade được sử dụng tốt nhất kết hợp với một ràng buộc duy nhất.

Khi nào nên sử dụng Cascading

  • Bạn có thể muốn nhận lại một xác nhận mạnh mẽ từ người dùng trước khi cho phép một hoạt động xếp tầng, nhưng nó phụ thuộc vào ứng dụng của bạn.
  • Cascading có thể khiến bạn gặp rắc rối nếu bạn cài đặt khóa ngoại sai. Nhưng bạn sẽ ổn nếu bạn làm đúng.
  • Thật không khôn ngoan khi sử dụng xếp tầng trước khi bạn hiểu kỹ về nó. Tuy nhiên, nó là một tính năng hữu ích và do đó đáng để dành thời gian để hiểu.

3
Lưu ý rằng các cập nhật theo tầng cũng thường được sử dụng khi các khóa tự nhiên "được gọi là" xuất hiện không phải là các khóa duy nhất có hiệu quả thực sự này. Trong thực tế, tôi tin rằng các cập nhật theo tầng chỉ cần thiết với các mô hình cơ sở dữ liệu được chuẩn hóa kém, và chúng là một cổng mở cho các bảng lộn xộn và mã lộn xộn.
Philippe Grondier

1
Bạn đang thiếu một điểm quan trọng, xếp tầng có thể tạo ra các vấn đề hiệu suất lớn nếu có nhiều hồ sơ con.
HLGEM

13
@HLGEM - Tôi không thấy sự liên quan. Nếu một hoạt động theo tầng gây ra sự chậm lại, quy trình thủ công tương đương sẽ gây ra sự chậm lại tương tự hoặc không được bảo vệ chính xác trong trường hợp giao dịch cần được khôi phục.
Joel Coehoorn

3
Tại sao việc cập nhật xếp tầng trên cột IDENTITY hoặc tự động gia tăng sẽ không thành vấn đề? Tôi có thể thấy lý do tại sao nó sẽ không cần thiết bởi vì bạn không cần phải thay đổi những giá trị (tùy ý), nhưng nếu một trong số họ đã làm thay đổi, ít nhất là sự toàn vẹn tham chiếu sẽ là nguyên vẹn.
Kenny Evitt

3
10 viên đạn? Bây giờ chúng ta biết Joel không bắn một khẩu súng lục ổ quay.
Neil N

68

Khóa ngoại là cách tốt nhất để đảm bảo tính toàn vẹn tham chiếu của cơ sở dữ liệu. Tránh các tầng do phép thuật cũng giống như viết mọi thứ trong hội đồng vì bạn không tin vào phép thuật đằng sau trình biên dịch.

Điều tồi tệ là việc sử dụng sai các khóa ngoại, chẳng hạn như tạo chúng ngược, chẳng hạn.

Ví dụ của Juan Manuel là ví dụ điển hình, nếu bạn sử dụng mã, sẽ có nhiều cơ hội để lại DocumentItems giả trong cơ sở dữ liệu sẽ đến và cắn bạn.

Ví dụ, cập nhật xếp tầng rất hữu ích, khi bạn có tham chiếu đến dữ liệu bằng thứ gì đó có thể thay đổi, giả sử khóa chính của bảng người dùng là tên, tổ hợp họ. Sau đó, bạn muốn thay đổi trong sự kết hợp đó để truyền đến bất cứ nơi nào chúng được tham chiếu.

@Aidan, sự rõ ràng mà bạn đề cập đến có chi phí cao, cơ hội để lại dữ liệu giả trong cơ sở dữ liệu của bạn, không phảinhỏ . Đối với tôi, nó thường chỉ thiếu quen thuộc với DB và không có khả năng tìm ra FK nào đang tồn tại trước khi làm việc với DB nuôi dưỡng nỗi sợ hãi đó. Hoặc là, hoặc lạm dụng liên tục thác, sử dụng nó trong trường hợp các thực thể không liên quan đến khái niệm hoặc nơi bạn phải lưu giữ lịch sử.


7
Sử dụng loại khóa chính 'tự nhiên' đó là một ý tưởng thực sự tồi tệ ngay từ đầu.
Nick Johnson

4
RE: Nhận xét hướng đến Aidan. Không, bỏ CASCADE trên FK không làm tăng cơ hội để lại dữ liệu giả. Nó làm giảm khả năng nhiều dữ liệu sẽ bị tác động bởi một lệnh hơn dự kiến ​​và tăng mã. Rời khỏi FK hoàn toàn để lại cơ hội dữ liệu giả.
Shannon Severance

5
Có ít nhất hai lần trong sự nghiệp của tôi đã thấy hậu quả đe dọa kinh doanh của việc xóa tầng bị hiểu lầm Tôi rất không thích sử dụng chúng trong tất cả các trường hợp ngoại trừ các trường hợp cắt rõ ràng nhất. Trong cả hai trường hợp, dữ liệu đã bị xóa do một tầng thực sự nên được giữ lại nhưng không - và nó bị mất không được phát hiện cho đến khi chu kỳ sao lưu bình thường mất khả năng khôi phục dễ dàng. Vinko là chính xác từ quan điểm hoàn toàn hợp lý, tuy nhiên trong thế giới thực, việc sử dụng các tầng cho thấy một khả năng sai lầm của con người và hậu quả không lường trước nhiều hơn tôi muốn.
Cruachan

1
Tôi thực sự đã mã hóa các hệ thống trong đó đưa ra quyết định quản lý rằng người dùng sẽ phải xóa rõ ràng tất cả trẻ em một cách chi tiết trước khi chúng có thể xóa chính chủ chỉ để buộc người dùng phải suy nghĩ. Không sử dụng thác khi logic một cách có thể là một loại hỏa hoạn tương tự.
Cruachan

6
@Cruachan: Quy tắc, theo quan điểm của tôi, rất đơn giản. Nếu dữ liệu không liên quan chặt chẽ đến mức vô dụng nếu không có dữ liệu gốc, thì nó không đảm bảo mối quan hệ xếp tầng. Đây là những gì tôi đã cố gắng giải quyết trong cụm từ cuối cùng trên câu trả lời của tôi.
Vinko Vrsalovic

17

Tôi không bao giờ sử dụng xóa tầng.

Nếu tôi muốn xóa cái gì đó khỏi cơ sở dữ liệu, tôi muốn nói rõ ràng với cơ sở dữ liệu những gì tôi muốn lấy ra.

Tất nhiên chúng là một chức năng có sẵn trong cơ sở dữ liệu và có thể đôi khi sử dụng chúng là ổn, ví dụ nếu bạn có bảng 'đặt hàng' và bảng 'orderItem', bạn có thể muốn xóa các mục khi xóa đặt hàng.

Tôi thích sự rõ ràng mà tôi nhận được từ việc thực hiện nó trong mã (hoặc thủ tục được lưu trữ) hơn là "phép thuật" xảy ra.

Vì lý do tương tự, tôi cũng không phải là một fan hâm mộ của kích hoạt.

Một điều cần chú ý là nếu bạn xóa một 'đơn hàng', bạn sẽ nhận được báo cáo '1 hàng bị ảnh hưởng' ngay cả khi xóa theo tầng đã xóa 50 'orderItem.


34
Tại sao không loại bỏ các khóa chính, quá? Bạn sẽ có được sự rõ ràng về việc đảm bảo các giá trị duy nhất trong mã của bạn.
MusiGenesis

4
@MusiGenesis, Aidan không ủng hộ việc loại bỏ FK. FK vẫn bảo vệ dữ liệu, nhưng không có CASCADE ON .... phép thuật bất ngờ không xảy ra.
Shannon Severance

7
@Vinko: Xóa và cập nhật có ngữ nghĩa mặc định được xác định rõ. Thay đổi hành vi thông qua một tầng hoặc kích hoạt để thực hiện nhiều công việc hơn sẽ có nhiều cơ hội được thực hiện hơn dự định. Không, tôi không làm việc mà không kiểm tra và có cơ sở dữ liệu của tôi được ghi lại. Nhưng tôi có nhớ từng mẩu tài liệu trong khi viết mã không? Nếu tôi muốn ngữ nghĩa cấp cao hơn, như xóa cha mẹ và con cái, tôi sẽ viết và sử dụng SP để làm điều đó.
Shannon Severance

3
@Vinko. Vấn đề của phép thuật không nằm ở các nhà phát triển hoặc DBA có năng lực, đó là với Joe, sau 5 năm, người được giao nhiệm vụ đơn giản khi DBA đi nghỉ và sau đó lấy dữ liệu của công ty mà không ai nhận ra. Cascades có vị trí của chúng, nhưng điều quan trọng là phải xem xét các trường hợp đầy đủ, bao gồm các yếu tố con người, trước khi triển khai chúng.
Cruachan

5
@Vinko: Tại sao SP 'Gasp'? SP chắc chắn là con đường để đi đến nơi cơ sở dữ liệu là tài sản quan trọng của công ty. Có một cuộc tranh luận mạnh mẽ trong các loại tình huống đang nói về việc hạn chế tất cả các truy cập dữ liệu vào SP, hoặc ít nhất là tất cả trừ Chọn. Xem câu trả lời của tôi trong stackoverflow.com/questions/1171769/ Từ
Cruachan

13

Tôi làm việc rất nhiều với xóa tầng.

Cảm giác tốt khi biết bất cứ ai làm việc chống lại cơ sở dữ liệu có thể không bao giờ để lại bất kỳ dữ liệu không mong muốn nào. Nếu sự phụ thuộc tăng lên, tôi chỉ cần thay đổi các ràng buộc trong sơ đồ trong Management Studio và tôi không phải điều chỉnh sp hoặc dataacces.

Điều đó nói rằng, tôi có 1 vấn đề với xóa tầng và đó là các tham chiếu vòng tròn. Điều này thường dẫn đến các phần của cơ sở dữ liệu không có xóa tầng.


1
Tôi biết điều này rất cũ, nhưng +1 khi đề cập đến vấn đề tham chiếu vòng tròn với CASCADE DELETE.
Mã Maverick

2
Xin tha thứ cho một câu hỏi không có thật: điều gì thực sự xảy ra nếu bạn có được một tài liệu tham khảo tròn?
Tim Lovell-Smith

10

Tôi làm rất nhiều công việc cơ sở dữ liệu và hiếm khi thấy tầng xóa xóa hữu ích. Một lần tôi đã sử dụng chúng một cách hiệu quả là trong cơ sở dữ liệu báo cáo được cập nhật bởi một công việc hàng đêm. Tôi đảm bảo rằng mọi dữ liệu thay đổi đều được nhập chính xác bằng cách xóa mọi bản ghi cấp cao nhất đã thay đổi kể từ lần nhập cuối cùng, sau đó nhập lại các bản ghi đã sửa đổi và bất kỳ thứ gì liên quan đến chúng. Nó giúp tôi không phải viết nhiều thao tác xóa phức tạp nhìn từ dưới lên trên cùng của cơ sở dữ liệu của tôi.

Tôi không coi việc xóa tầng là khá tệ như trình kích hoạt vì chúng chỉ xóa dữ liệu, trình kích hoạt có thể có tất cả các loại nội dung khó chịu bên trong.

Nói chung, tôi tránh xóa thực sự hoàn toàn và sử dụng xóa logic (nghĩa là có một cột bit được gọi là isDelatted được đặt thành true) thay vào đó.


2
Bạn khiến tôi tò mò muốn tìm hiểu thêm. Tại sao bạn mạnh mẽ thích xóa logic? Dữ liệu bạn đang làm việc có liên quan gì không?
Tim Lovell-Smith

9

Một ví dụ là khi bạn có sự phụ thuộc giữa các thực thể ... ví dụ: Document -> DocumentItems (khi bạn xóa Document, DocumentItems không có lý do để tồn tại)


5

Sử dụng xóa tầng trong đó bạn muốn xóa bản ghi với FK nếu bản ghi PK tham chiếu của nó bị xóa. Nói cách khác, nơi bản ghi là vô nghĩa nếu không có bản ghi tham chiếu.

Tôi thấy xóa tầng rất hữu ích để đảm bảo rằng các tham chiếu chết được xóa theo mặc định thay vì gây ra các ngoại lệ null.


5

BẬT Xóa Cascade:

Khi bạn muốn các hàng trong bảng con bị xóa Nếu hàng tương ứng bị xóa trong bảng cha.

Nếu xóa tầng không được sử dụng thì sẽ xảy ra lỗi đối với tính toàn vẹn tham chiếu .

Cập nhật Cascade:

Khi bạn muốn thay đổi khóa chính được cập nhật bằng khóa ngoại


5

Tôi đã nghe nói về các DBA và / hoặc "Chính sách công ty" cấm sử dụng "Bật xóa tầng" (và những người khác) hoàn toàn vì những trải nghiệm tồi tệ trong quá khứ. Trong một trường hợp, một anh chàng đã viết ba kích hoạt mà cuối cùng lại gọi nhau. Ba ngày để phục hồi dẫn đến lệnh cấm hoàn toàn các tác nhân, tất cả chỉ vì những hành động của một thành ngữ.

Tất nhiên, đôi khi cần có Triggers thay vì "On Delete cascade", như khi một số dữ liệu con cần được bảo tồn. Nhưng trong các trường hợp khác, nó hoàn toàn hợp lệ để sử dụng phương pháp xếp tầng Xóa. Một lợi thế chính của "Bật xóa tầng" là nó bắt được TẤT CẢ các trẻ em; một thủ tục kích hoạt / lưu trữ bằng văn bản tùy chỉnh có thể không nếu nó không được mã hóa chính xác.

Tôi tin rằng Nhà phát triển nên được phép đưa ra quyết định dựa trên sự phát triển là gì và thông số kỹ thuật nói gì. Lệnh cấm thảm dựa trên trải nghiệm xấu không nên là tiêu chí; quá trình suy nghĩ "Không bao giờ sử dụng" là tốt nhất. Một cuộc gọi phán xét cần phải được thực hiện mỗi lần và những thay đổi được thực hiện khi mô hình kinh doanh thay đổi.

Đây không phải là những gì phát triển là tất cả về?


Tôi không nghĩ rằng nó sẽ xóa tất cả mọi thứ ... bạn có nghĩa là tính năng thực sự làm những gì nó nói? ...
Joel Coehoorn

3

Một lý do để đưa vào xóa tầng (thay vì thực hiện trong mã) là để cải thiện hiệu suất.

Trường hợp 1: Với một tầng xóa

 DELETE FROM table WHERE SomeDate < 7 years ago;

Trường hợp 2: Không xóa tầng

 FOR EACH R IN (SELECT FROM table WHERE SomeDate < 7 years ago) LOOP
   DELETE FROM ChildTable WHERE tableId = R.tableId;
   DELETE FROM table WHERE tableId = R.tableid;
   /* More child tables here */
 NEXT

Thứ hai, khi bạn thêm vào một bảng con bổ sung có xóa tầng, mã trong Trường hợp 1 tiếp tục hoạt động.

Tôi sẽ chỉ đưa vào một thác trong đó ngữ nghĩa của mối quan hệ là "một phần của". Nếu không, một số kẻ ngốc sẽ xóa một nửa cơ sở dữ liệu của bạn khi bạn làm:

DELETE FROM CURRENCY WHERE CurrencyCode = 'USD'

5
Không biết bạn sử dụng cơ sở dữ liệu nào, tôi sẽ đề nghị xóa thủ công của bạn hoạt động kém hơn so với xóa tầng vì nó không được đặt dựa trên. Trong hầu hết các cơ sở dữ liệu, bạn có thể xóa dựa trên một liên kết đến một bảng khác và do đó, xóa dựa trên tập hợp, nhanh hơn nhiều so với lặp qua các bản ghi.
HLGEM

2

Tôi cố gắng tránh xóa hoặc cập nhật mà tôi không yêu cầu rõ ràng trong máy chủ SQL.

Hoặc thông qua xếp tầng hoặc thông qua việc sử dụng các kích hoạt. Họ có xu hướng cắn bạn vào mông một lúc nào đó, hoặc khi cố gắng theo dõi một lỗi hoặc khi chẩn đoán các vấn đề về hiệu suất.

Nơi tôi sẽ sử dụng chúng là để đảm bảo tính nhất quán cho không nhiều nỗ lực. Để có được hiệu quả tương tự, bạn sẽ phải sử dụng các thủ tục được lưu trữ.


2

Tôi cũng như mọi người khác ở đây, thấy rằng việc xóa tầng thực sự chỉ hữu ích một chút (thực sự không có nhiều công việc để xóa dữ liệu được tham chiếu trong các bảng khác - nếu có nhiều bảng, bạn chỉ cần tự động hóa điều này bằng một tập lệnh) nhưng thực sự gây phiền nhiễu khi ai đó vô tình xếp tầng xóa một số dữ liệu quan trọng khó khôi phục.

Trường hợp duy nhất tôi sử dụng là nếu dữ liệu trong bảng bảng được kiểm soát cao (ví dụ: quyền hạn chế) và chỉ được cập nhật hoặc xóa thông qua quy trình được kiểm soát (như cập nhật phần mềm) đã được xác minh.


1

Việc xóa hoặc cập nhật lên S để loại bỏ giá trị khóa ngoài được tìm thấy trong một số bộ dữ liệu R có thể được xử lý theo một trong ba cách sau:

  1. Sự từ chối
  2. Lan truyền
  3. vô hiệu hóa.

Tuyên truyền được gọi là tầng.

Có hai trường hợp:

Nếu một bộ dữ liệu trong S bị xóa, hãy xóa bộ dữ liệu R được đề cập đến.

Nếu một bộ dữ liệu trong S được cập nhật, hãy cập nhật giá trị trong bộ dữ liệu R tham chiếu đến nó.


0

Nếu bạn đang làm việc trên một hệ thống có nhiều mô-đun khác nhau trong các phiên bản khác nhau, điều đó có thể rất hữu ích, nếu các mục bị xóa theo tầng là một phần / thuộc sở hữu của chủ sở hữu PK. Khác, tất cả các mô-đun sẽ yêu cầu các bản vá ngay lập tức để dọn sạch các mục phụ thuộc của chúng trước khi xóa chủ sở hữu PK hoặc quan hệ khóa ngoại sẽ bị bỏ qua hoàn toàn, có thể để lại hàng tấn rác trong hệ thống nếu việc dọn dẹp không được thực hiện đúng.

Tôi vừa giới thiệu xóa tầng cho một bảng giao lộ mới giữa hai bảng đã có sẵn (chỉ giao cắt để xóa), sau khi xóa tầng đã được khuyến khích từ khá lâu. Nó cũng không quá tệ nếu dữ liệu bị mất.

Tuy nhiên, đó là một điều tồi tệ trên các bảng danh sách giống như enum: ai đó xóa mục 13 - màu vàng khỏi "màu" của bảng và tất cả các mục màu vàng trong cơ sở dữ liệu sẽ bị xóa. Ngoài ra, đôi khi chúng được cập nhật theo cách xóa tất cả chèn, dẫn đến tính toàn vẹn tham chiếu hoàn toàn bị bỏ qua. Tất nhiên là sai, nhưng bạn sẽ thay đổi một phần mềm phức tạp đã chạy trong nhiều năm như thế nào, với việc giới thiệu tính toàn vẹn tham chiếu thực sự có nguy cơ bị tác dụng phụ không mong muốn?

Một vấn đề khác là khi các giá trị khóa ngoài gốc sẽ được giữ ngay cả sau khi khóa chính bị xóa. Người ta có thể tạo một cột bia mộ và tùy chọn ON DELETE SET NULL cho FK ban đầu, nhưng điều này một lần nữa yêu cầu kích hoạt hoặc mã cụ thể để duy trì giá trị khóa dự phòng (trừ sau khi xóa PK).


0

Xóa tầng là cực kỳ hữu ích khi thực hiện các thực thể siêu loại và loại phụ logic trong cơ sở dữ liệu vật lý.

Khi các bảng siêu loại và loại phụ riêng biệt được sử dụng để triển khai thực tế các loại siêu loại / loại phụ (trái ngược với việc cuộn tất cả các thuộc tính loại phụ vào một bảng siêu loại vật lý duy nhất), có một loại mối quan hệ giữa các bảng này và vấn đề sau đó trở thành cách giữ các khóa chính đồng bộ 100% giữa các bảng này.

Xóa tầng có thể là một công cụ rất hữu ích để:

1) Đảm bảo rằng việc xóa một bản ghi loại siêu cũng xóa bản ghi loại phụ đơn tương ứng.

2) Đảm bảo rằng mọi bản ghi loại phụ cũng xóa bản ghi loại siêu. Điều này đạt được bằng cách thực hiện trình kích hoạt xóa "thay vì" trên bảng loại phụ đi và xóa bản ghi siêu loại tương ứng, do đó, tầng này sẽ xóa bản ghi loại phụ.

Sử dụng xóa tầng theo cách này đảm bảo rằng không có bản ghi loại siêu hoặc loại phụ mồ côi nào tồn tại, bất kể bạn xóa bản ghi siêu loại trước hay bản ghi loại phụ trước.


Ví dụ tốt. Trong JPA, đó là Bảng kế thừa kế thừa. Đối với 1): Thông thường, bạn đang sử dụng khung lớp liên tục (EclipseLink, Hibernate, ...), thực hiện chuỗi đã xóa cho một thực thể được nối để trước tiên xóa phần được nối, sau đó là phần siêu. Nhưng nếu bạn có sẵn phần mềm cơ bản hơn, như công việc nhập hoặc lưu trữ, thật tiện lợi khi có thể xóa thực thể bằng cách xóa phần trên siêu phần. Về 2): đồng ý, nhưng trong trường hợp đó, khách hàng nên biết rằng anh ta đang làm việc trên một phần tham gia / phụ của thực thể.
leo
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.