Trang 1 / 2 12 LastLast
Hiển thị kết quả từ 1 đến 10 / 14
  1. #1
    Tham gia
    20-02-2009
    Location
    Ninh Bình
    Bài viết
    567
    Like
    0
    Thanked 34 Times in 31 Posts

    Ý tưởng mới ! Bảng Danh Sách Tên Họ Theo Thứ Tự ABC

    Một trong những điều "khó chịu" nhất của CSDL tại Việt Nam nói riêng, và tại những quốc gia dùng mẫu tự La-tinh có bỏ dấu nói chung, là phải tìm cách sắp xếp bảng (Table) theo thứ tự ABC...

    Hoa Kỳ là quốc gia đầu tiên sáng lập lên bảng mẫu tự ASCII, và cũng là một quốc gia tiền phong sáng lập ra những CSDL như ORACLES, MS-SQL, SyBase, cho nên việc sắp xếp mẫu tự theo thứ tự cho những mẫu tự có bỏ dấu không được họ nghiên cứu tới. Do đó, khi viết một lập trình CSDL cho tiếng Việt, chúng ta gặp nhiều khó khăn trong việc trình bày một bảng dữ liệu theo thứ tự ABC.

    Việc trình bày một bảng DL theo thứ tự ABC với Anh-ngữ thật dễ dàng như sau:

    SELECT first_name, last_name FROM PERSON (NOLOCK)
    ORDER BY first_name, last_name

    Bởi vì tiếng Anh không có dấu, cho nên lệnh từ SELECT ở trên sẽ cho chúng ta một bảng dữ liệu tên họ theo thứ tự. Nhưng nếu hồ sơ PERSON chứa đựng tên họ của người Việt như Hồng Nguyễn, Hải Trần, thì thứ tự ABC sẽ bị đảo lộn vô trật tự, rất khó coi. Tôi đã gặp nhiều lập trình tiếng Việt vấp phải vấn đề này, và đây là một vấn đề không dễ giải quyết.

    Trong bài ra mắt (đầu tiên) trên diễn đàn này, tôi mạo muội trình bày một phương án có thể giải đáp được vấn đề thứ tự ABC cho một CSDL tiếng Việt. Trước khi đề ra phương án,tôi cũng xin được thẳng thắn nói trước rằng, trong lãnh vực khoa học, không ai có thể tự hào rằng phương án của mình là phương án hay nhất, tốt nhất. Người Mỹ có câu tục ngữ đại ý rằng "luôn luôn có nhiều cách để giải quyết một vấn đề." Do đó, nếu chỉ một, hai người trong bạn có thể dùng phương án của tôi vào lập trình của các bạn, tôi lấy điều đó làm vui. Ngược lại, nếu có bạn nào có phương án hay hơn, hoặc thấy phương án của tôi có những khuyết điểm, cũng xin vui lòng chỉ điểm, để chúng ta cùng học hỏi.

    Xin trở lại vấn đề chính. Nếu các bạn có một hồ sơ (table) như sau:

    CREATE TABLE dbo.PERSON
    (
    ten NVARCHAR(20) NOT NULL,
    ten_lot NVARCHAR(10) NULL,
    ten_ho NVARCHAR(20) NOT NULL,
    CONSTRAINT [PK_PERSON] PRIMARY KEY NONCLUSTERED (ten,ten_lot,ten_ho)
    )

    Chắc chắn các bạn sẽ không thể trình bày một danh sách theo thứ tự ABC. Lý do rất giản dị vì lệnh từ ORDER BY sắp xếp thứ tự theo mã số của Unicode. Vậy thì, câu hỏi là làm sao chúng ta có thể có một bảng danh sách theo thứ tự ABC của tiếng Việt? Hơn thế nữa, khi viết một lập trình CSDL, tốc độ xử lý cũng không thể không nghĩ tới. Câu hỏi trở nên hóc búa hơn: Làm sao để có thể trình bày một bảng danh sách họ tên theo thứ tự ABC với tốc độ xử lý nhanh chóng nhất?

    Phương án giải quyết của tôi như sau:

    1. Chúng ta cấu tạo lại hồ sơ (table) PERSON như sau:

    CREATE TABLE dbo.PERSON
    (
    tenho_abc NVARCHAR(60) NOT NULL,
    ten NVARCHAR(20) NOT NULL,
    ten_lot NVARCHAR(10) NULL,
    ten_ho NVARCHAR(20) NOT NULL,
    CONSTRAINT [PK_PERSON] PRIMARY KEY NONCLUSTERED (tenho_abc)
    )

    Như đã thấy ở trên, tôi cấu tạo thêm một danh mục (column) nữa với tên là tenho_abc. Tôi cũng thay đổi khóa chính (primary key) của hồ sơ để tốc độ xử lý có thể tốt hơn. Dĩ nhiên, nếu đây là một hồ sơ tại nơi làm việc, tôi cũng sẽ đặt thêm những khóa phụ (secondary key) cho ten và ten_ho để cho việc tìm kiếm (look-up) cá nhân được mau chóng hơn.

    2. Tạo một function để mỗi khi tên, ten_lot, và ten_ho thay đổi, thì tenho_abc cũng thay đổi theo. Chúng ta có thể dùng TRIGGER vào việc này. Nhưng TRIGGER cũng là một con dao hai lưỡi - vào một dịp khác tôi sẽ trình bày tại sao trigger là một con dao hai lưỡi. Cho nên, tôi sẽ tránh không dùng trigger. function dùng để xử lý tenho_abc như sau:

    IF EXISTS (SELECT * FROM dbo.sysobjects where id = object_id(N'[dbo].[fnc_get_sort_string]') and xtype in (N'FN', N'IF', N'TF'))
    DROP FUNCTION [dbo].[fnc_get_sort_string]
    GO

    CREATE FUNCTION [dbo].[fnc_get_sort_string]
    (
    @szLastName NVARCHAR(MAX),
    @szMidName NVARCHAR(MAX),
    @szFirstName NVARCHAR(MAX)
    )
    RETURNS NVARCHAR(MAX)
    --WITH ENCRYPTION
    AS
    BEGIN
    DECLARE @idx INT
    DECLARE @szLen INT
    DECLARE @iFound INT
    DECLARE @iRemain INT
    DECLARE @szVows NVARCHAR(MAX)
    DECLARE @szRetString NVARCHAR(MAX)
    DECLARE @szName NVARCHAR(MAX)
    DECLARE @szChar NCHAR


    SET @szRetString = N''
    SET @szName = Lower(LTRIM(@szLastName) + LTRIM(@szMidName) + LTRIM(@szFirstName))
    SET @szVows = N'aáàảãạăắằẳẵặâấầẩẫậe éèẻẽẹêếềểễệiíìỉĩịoóò õọơớờởỡợôốồổỗộuúùủũ ụưứừửữựýỳỷỹỵdđ'
    SET @idx = 0
    SET @szLen = LEN(@szName)
    WHILE(@idx < @szLen)
    BEGIN
    SET @idx = @idx + 1
    SET @szChar = SUBSTRING(@szName,@idx,1)
    SET @iFound = CHARINDEX(@szChar,@szVows)
    IF (@iFound > 0)
    BEGIN
    SET @iRemain = @iFound % 6
    IF(0 < @iRemain)
    BEGIN
    SET @iFound = (@iFound / 6) + 1
    END
    ELSE
    BEGIN
    SET @iFound = @iFound / 6
    SET @iRemain = 6
    END

    IF(@iFound = 1)
    BEGIN
    SET @szRetString = @szRetString + N'a'
    SET @szRetString = @szRetString + NCHAR(65 + @iRemain)
    END
    ELSE IF (@iFound = 2)
    BEGIN
    SET @szRetString = @szRetString + N'a'
    SET @szRetString = @szRetString + NCHAR(65 + @iRemain + 10)
    END
    ELSE IF (@iFound = 3)
    BEGIN
    SET @szRetString = @szRetString + N'a'
    SET @szRetString = @szRetString + NCHAR(65 + @iRemain + 20)
    END
    ELSE IF (@iFound = 4)
    BEGIN
    SET @szRetString = @szRetString + N'e'
    SET @szRetString = @szRetString + NCHAR(65 + @iRemain)
    END
    ELSE IF (@iFound = 5)
    BEGIN
    SET @szRetString = @szRetString + N'e'
    SET @szRetString = @szRetString + NCHAR(65 + @iRemain + 10)
    END
    ELSE IF (@iFound = 6)
    BEGIN
    SET @szRetString = @szRetString + N'i'
    SET @szRetString = @szRetString + NCHAR(65 + @iRemain)
    END
    ELSE IF (@iFound = 7)
    BEGIN
    SET @szRetString = @szRetString + N'o' + NCHAR(65 + @iRemain)
    END
    ELSE IF (@iFound = 8)
    BEGIN
    SET @szRetString = @szRetString + N'o' + NCHAR(65 + @iRemain + 10)
    END
    ELSE IF (@iFound = 9)
    BEGIN
    SET @szRetString = @szRetString + N'o' + NCHAR(65 + @iRemain + 20)
    END
    ELSE IF (@iFound = 10)
    BEGIN
    SET @szRetString = @szRetString + N'u' + NCHAR(65 + @iRemain)
    END
    ELSE IF (@iFound = 11)
    BEGIN
    SET @szRetString = @szRetString + N'u' + NCHAR(65 + @iRemain + 10)
    END
    ELSE IF (@iFound = 12)
    BEGIN
    SET @szRetString = @szRetString + N'y' + NCHAR(65 + @iRemain)
    END
    ELSE IF (@iFound = 13)
    BEGIN
    SET @szRetString = @szRetString + N'd' + NCHAR(65 + @iRemain)
    END

    END
    ELSE
    BEGIN
    SET @szRetString = @szRetString + @szChar
    END
    END
    RETURN @szRetString
    END

    *** Dẫn giải

    a) Lệnh từ SQL đầu tiên IF EXISTS(...) dùng để cập nhật hóa function. Xin nhớ ký, nếu bạn là một lập trình gia thực thụ dùng máu xám để nuôi gia đình, thì chắc chắn các bạn cần biết, khi viết một function, hay stored procedure, các bạn phải có phương án để cập nhật chúng. Lệnh từ đầu tiên này dùng để xóa bỏ function isp_get_sort_string nếu nó đang tồn tại trong CSDL. Nếu không có lệnh từ này, thì lệnh từ kế tiếp sẽ bị lỗi nếu function isp_get_sort_string đã có sẵn trong CSDL.

    b) lệnh từ CREATE FUNCTION (...) dùng để cấu tạo function isp_get_sort_string. Các bạn có thể thay đổi tên của nó, chẳng hạn như isp_tạo_tenho_abc(). Các bạn cũng có thể thay đổi tên của những biến-từ (variable), như @szFirstName thành @ten, v.v... Điều quan trọng ở đây là không bao giờ đặt tên của function hay stored procudure bắt đầu bằng hai mẫu tự SP..., vì nó sẽ làm giảm tốc độ xử lý. Lý do là mỗi khi MS-SQL xử lý một stored procedure có tên bắt đầu bằng SP, MS-SQL sẽ tìm cái procedure đó ở CSDL master trước.

    Trong một bài khác, tôi sẽ giải tường tận từng hàng một cho function isp_get_sort_string. Nhưng trong phạm vi của bài này, tôi chỉ xin tóm tắt như sau: function isp_get_sort_string() nhận tên,tên lót, và tên họ, và trả lại một biến từ có cả chữ gom lại cộng thêm những mẫu tự sắp xếp để chúng ta có thể có một bảng danh sách tên và họ theo thứ tự ABC. Tôi cũng khuyến khích các bạn tự tìm hiểu bằng cách nghiên cứu những lệnh từ trong function này, và gửi cho tôi những câu hỏi. Nếu số người hỏi nhiều,tôi sẽ có một bài viết khác để trả lời chung thật sớm.. Nếu không có ai hỏi, tôi sẽ tự biết rằng tôi không có nhiều độc giả, và dĩ nhiên, bài viết kế tiếp của tôi sẽ không ra đời sớm sủa (theo đúng luật cung cầu).

    Tóm tắt lại, function isp_get_sort_string() sẽ cho chúng ta một biến-từ để chúng ta có thể giữ trong danh mục tenho_abc.


    3. INSERT và UPDATE
    Khi bỏ tên tuổi vào hồ sơ (INSERT), hoặc khi sửa chữa tên tuổi (UPDATE), chúng ta cần phải xử lý danh mục tenho_abc, để danh mục này có những dữ kiện phù hợp với ten, ten_lot, và ten_ho.

    a) INSERT
    mỗi khi cho thêm một danh mục vào hồ sơ, các bạn cần phải xử lý tenho_abc, thí dụ như sau:

    INSERT dbo.PERSON (tenho_abc, ten, ten_lot,ten_ho)
    VALUES(EXEC dbo.isp_get_sort_string('Hồng','Đào','Nguyễn ') ,'Hồng','Đào','Nguyễn')


    b) UPDATE
    UPDATE dbo.PERSON
    SET tenho_abc = EXEC dbo.isp_get_sort_string('Hồng','Đào','Trần')
    ten = 'Hồng',
    ten_lot = 'Đào',
    tên_ho = 'Trần'
    WHERE tenho_abc = (EXEC dbo.isp_get_sort_string('Hồng','Đào','Nguyễn '))


    4. Lấy danh sách theo mẫu tự:

    Những việc khó khăn đã làm xong. Bây giờ, để có một danh sách tên họ theo thứ tự ABC sẽ trở lên dễ dàng với nhiều thích thú. Các bạn chỉ dùng lệnh từ SELECT như sau:

    SELECT ten,ten_lot, ten_ho FROM PERSON (NOLOCK)
    ORDER BY tenho_abc

    SQL sẽ cho các bạn một danh sách theo thứ tự ABC của tên, tên đệm, và họ. Áp dụng phương án này vào lập trình ABC, các bạn chắc chắn sẽ làm cho khách hàng vừa lòng.

    Chúc tất cả.
    DQ

    Tác-quyền(CopyRight): Bài viết và những phương án lập trình trong bài viết này có thể dùng bất cứ ở đâu với bất cứ mục đích gì. Nhưng tên của tác giả phải được chú thích mỗi khi dùng.
    Quote Quote

  2. #2
    Tham gia
    23-06-2006
    Bài viết
    204
    Like
    0
    Thanked 3 Times in 2 Posts
    Chào mừng bạn đã tham gia vào diễn đàn này. Bài viết của bạn rất hay đấy. Tuy nhiên tôi thấy có một số vấn đề sau:

    1. Bạn đã nhầm lẫn với giải pháp dùng sp trong các ví dụ của bạn (INSERT, UPDATE)
    2. Trong ví dụ, bạn quên dùng tiền tố N cho mỗi chuỗi ký tự
    3. Không thấy bạn khai báo hằng số MAX ở đâu cả. Tôi phải thay thế MAX bằng một con số nào đấy (vd 256) thì mới chạy
    4. Cách sắp xếp vẫn có chỗ chưa ổn. Ví dụ thông < thôn < thô trong khi ngược lại mới đúng.

    Bạn sửa lại đi nhé.

  3. #3
    Tham gia
    01-01-2008
    Location
    Thiên đường hạnh phúc
    Bài viết
    1,299
    Like
    9
    Thanked 127 Times in 67 Posts
    Cám ơn bạn nhiều. Trên DDTH càng ngày càng ít những bài như vậy. Hy vọng bạn còn nhiều bài hay nữa.
    Thế là có thêm một giải pháp từ bài của bạn rồi.

  4. #4
    Tham gia
    20-02-2009
    Location
    Ninh Bình
    Bài viết
    567
    Like
    0
    Thanked 34 Times in 31 Posts
    Cám ơn bạn VQT.
    Xin vui lòng xem trả lời ở dưới.


    Quote Được gửi bởi vqt View Post
    Chào mừng bạn đã tham gia vào diễn đàn này. Bài viết của bạn rất hay đấy. Tuy nhiên tôi thấy có một số vấn đề sau:

    1. Bạn đã nhầm lẫn với giải pháp dùng sp trong các ví dụ của bạn (INSERT, UPDATE)
    2. Trong ví dụ, bạn quên dùng tiền tố N cho mỗi chuỗi ký tự

    Cám ơn. không có "tiền tố" cho các lệnh từ UPDATE,INSERT.

    3. Không thấy bạn khai báo hằng số MAX ở đâu cả. Tôi phải thay thế MAX bằng một con số nào đấy (vd 256) thì mới chạy

    MAX chỉ có ở SQL2005/2008 mà thôi. nếu bạn dùng SQL2000 thì có thể thay thế bằng 256

    4. Cách sắp xếp vẫn có chỗ chưa ổn. Ví dụ thông < thôn < thô trong khi ngược lại mới đúng.

    Không tìm thấy thông < thôn < thô ở đâu hết. Bạn có thể giải thích tường tận hơn một chút không.

    Bạn sửa lại đi nhé.
    Làm biếng quá! Khất lần sau nha.
    4
    Được sửa bởi dq_ninh lúc 14:11 ngày 21-02-2009

  5. #5
    Tham gia
    18-08-2005
    Location
    Montclair
    Bài viết
    8,722
    Like
    20
    Thanked 1,457 Times in 600 Posts
    Chữ VN có 94 chữ (tôi chưa nói tới chữ HOA), và nó gồm dấu cách (space) và 93 alphabet như sau coi list :

    " aáàảãạăắằẳẵặâấầẩẫậbcd đeéèẻẽẹêếềểễệfghiíìỉĩịj klmnoóòỏõọơớờởỡợôốồổỗ pqrstuúùủũụưứừửữựvwxýỳỷỹ ỵz"

    Vị trí dấu cách (space) trong string này là 1.
    Vị trí chữ a trong string này là 2.
    Vị trí chữ á trong string này là 3.
    Vị trí chữ à trong string này là 4.
    tiếp tục .... v.v.v

    Tạo một function thutu(stringVN) với đặc điểm như sau:

    Đọc từ character của StringVN. Ví đụ thutu("nguyễn văn bá")


    Thế chử "n" đầu tiên cũa "nguyễn văn bá" với chr(19+vị trí của "n" trong 94 chữ VN ở trên).


    Thế chữ "n" với chr(19+49) 49 là vị trí cũa "n" trong 94 chữ Việt.
    Thế chữ "g" với chr(19+37), 37 là vị trí của "g" trong 94 chữ Việt.
    Thế chữ "u" với chr(19+73), 73 là vị trí của "u" trong 94 chữ Việt.
    Thế chữ "y" với chr(19+88), 88 là vị trí của "y" trong 94 chữ Việt.
    Thế chữ "" với chr(19+34), 34 là vị trí của "" trong 94 chữ Việt.
    Thế chữ "n" với chr(19+49), 49 là vị trí của "n" trong 94 chữ Việt.
    Thế space " " với chr(19+1), 1 là vị trí của " " (space) trong 94 chữ Việt.

    và tiếp tục tới cho hết cái tên nguyễn văn bá.

    và return các ký tự string để bạn sort hay index

    String đó hiển thị kỳ cục nhưng nó đi theo thứ tự chữ VN.

    Và khi bạn sort hay index cái tên do funtion thutu() return sẻ đi theo thứ tự tiếng VN đúng.


    Cách thức này áp dụng cho mọi ngôn ngữ và mọi loại font.

    Sorry là tôi không có học cách lập trình theo VN nên không đủ từ IT để trình bày cho rỏ ràng.

  6. #6
    Tham gia
    01-01-2008
    Location
    Thiên đường hạnh phúc
    Bài viết
    1,299
    Like
    9
    Thanked 127 Times in 67 Posts
    Nể ông già này thật đấy.
    Thuật toán bác TOM đưa ra có thể áp dụng xây dựng hàm [thutu(string)] ở phía database và phía application. Nếu xây dựng ở phía application thì CSDL nào cũng xài được (bất kể encoding của database nữa chứ).
    Sắp xếp họ tên chắc chẳng cần chữ HOA hay thường. Chuỗi 94 ký tự Việt theo già TOM chắc phải như vầy:
    Code:
    " aáàảãạăắằẳẵặâấầẩẫậbcdđeéèẻẽẹêếềểễệfghiíìỉĩịjklmnoóòỏõọơớờởỡợôốồổõộpqrstuúùủũụưứừửữựvwxýỳỷỹỵz"
    Ví dụ dưới đây xin giải thích thêm một tí (có thể có bạn chưa rõ):
    Dùng hàm (hay vòng lặp thì chính xác hơn) xác định vị trí ký tự n từ chuỗi cần index (nếu có ký tự n này) trong chuỗi 94 ký tự, rồi đặt vào chỗ 49 thì đúng hơn
    Code:
    Thế chữ "n" với chr(19+49) 49 là vị trí cũa "n" trong 94 chữ Việt.
    @TOM: bác gái giận hay sao mà dạo này không thấy bác TOM trình bày món ăn nào mới cả?
    Được sửa bởi bachnga lúc 06:42 ngày 22-02-2009

  7. #7
    Tham gia
    23-06-2006
    Bài viết
    204
    Like
    0
    Thanked 3 Times in 2 Posts
    Chào bạn dq_ninh

    Về vấn đề số 2. Trong ví dụ INSERT, UPDATE của bạn có dùng một số chuỗi 'Hồng', 'Đào', 'Trần'... Nếu bạn không đặt tiền tố N vào trước nó thì SQL sẽ ko hiểu đó là dữ liệu Unicode. Cái này chắc bạn quên.

    Vấn đề 4. Bạn cứ thử (insert) với 3 cái tên tôi đưa ra ở trên rồi dùng chính câu lệnh (select) của bạn là sẽ thấy thứ tự đảo ngược thôi mà.

  8. #8
    Tham gia
    22-12-2003
    Bài viết
    187
    Like
    0
    Thanked 4 Times in 3 Posts
    Em gà xin các bác thông cảm nhưng em muốn biết nên phải hỏi :

    Thế chử "n" đầu tiên cũa "nguyễn văn bá" với chr(19+vị trí của "n" trong 94 chữ VN ở trên).

    Số 19 ở đâu ra vậy ạ ?

  9. #9
    Tham gia
    18-08-2005
    Location
    Montclair
    Bài viết
    8,722
    Like
    20
    Thanked 1,457 Times in 600 Posts
    Tại vì tôi muốn sấp sếp kể từ chr(20) trở về sau. Nên tôi mới chọn Chr(19), bạn có thề dùng chr(20) hay 21,22,23,24 ,25,40,50,100 v.v cũng được.
    Miễn sao chr(x+94) (vị trí cuối) không quá 255 characters của font của VNI (unicode thì có hơn 255 characters nên mình không lo).

  10. #10
    Tham gia
    20-02-2009
    Location
    Ninh Bình
    Bài viết
    567
    Like
    0
    Thanked 34 Times in 31 Posts
    Bạn VQT,

    Rất cám ơn bạn đã rất "tiểu tiết". Vấn đề chính ở đây là cái ý tưởng (idea) của phương án, và cái function isp_get_sort_string() mà tôi đã bỏ ra hết mấy tiếng đồng hồ để viết.

    Khi tôi đưa cái ý tưởng và cái function isp_get_sort_string(), tôi chỉ bỏ ra 5 phút đồng hồ viết mấy cái thí dụ cho INSERT và UPDATE, không khỏi có những thiếu sót.

    Dĩ nhiên, không ai cóp py mấy dòng INSERT và UPDATE đó, nhưng tôi nghĩ là có nhiều người sẽ dùng cái function isp_get_sort_string(). Cho nên, việc thêm cái "tiền tố" 'N' ở đằng trước mấy cái string chỉ là chuyện nhỏ, tưởng không cần thiết phải lập đi lập lại nhiều lần.

    Tôi thất vọng vì bạn không thấy được những điều có thể bổ sung cho function function isp_get_sort_string(), bởi vì khi viết nó xong, tôi ngồi nhìn ngắm một chút và nhận thấy tôi có thể thay đổi vài lệnh từ cho nó ngắn hơn mà mau hơn.

    Xin nhắc lại, việc thiếu mấy cái "tiền tồ" chỉ là một chuyện nhỏ. Tôi cũng đã gửi bài này lên mấy cái mạng tiếng Anh, và người nước ngoài không ai thèm để ý đến mấy cái tiểu tiết vụn vặt đó.

    Cheer!

    Quote Được gửi bởi vqt View Post
    Chào bạn dq_ninh

    Về vấn đề số 2. Trong ví dụ INSERT, UPDATE của bạn có dùng một số chuỗi 'Hồng', 'Đào', 'Trần'... Nếu bạn không đặt tiền tố N vào trước nó thì SQL sẽ ko hiểu đó là dữ liệu Unicode. Cái này chắc bạn quên.

    Vấn đề 4. Bạn cứ thử (insert) với 3 cái tên tôi đưa ra ở trên rồi dùng chính câu lệnh (select) của bạn là sẽ thấy thứ tự đảo ngược thôi mà.

Trang 1 / 2 12 LastLast

Bookmarks

Quy định

  • Bạn không thể tạo chủ đề mới
  • Bạn không thể trả lời bài viết
  • Bạn không thể gửi file đính kèm
  • Bạn không thể sửa bài viết của mình
  •