Chia giá trị từ một trường thành hai


125

Tôi đã có một trường bảng membernamechứa cả tên cuối cùng và tên người dùng. Có thể chia những thành 2 lĩnh vực memberfirst, memberlast?

Tất cả các bản ghi có định dạng "Tên họ" (không có dấu ngoặc kép và khoảng trắng ở giữa).


6
"Tất cả các bản ghi có định dạng này" Tên họ "(không có dấu ngoặc kép và khoảng trắng ở giữa)." ... Thật kỳ diệu ... Làm ơn, làm ơn , đừng quên những người như tôi khi đưa ra quyết định cơ sở dữ liệu. Rất thường xuyên, tôi nhận được các trang web cho tôi biết họ của tôi có chứa một ký tự (sic) bất hợp pháp ... :(
Stijn de Witt

@StijndeWitt Bạn nói chung là đúng, tuy nhiên có vẻ như cơ sở dữ liệu này không chứa tên của bạn, ít nhất là không ở dạng chính thức. Ở nước tôi, họ được viết đầu tiên, vì vậy tôi cũng sẽ bị "phân biệt đối xử" trong bảng dữ liệu này. Chỉ cần nhìn thấy điều này ->
Dávid Horváth

Câu trả lời:


226

Thật không may, MySQL không có chức năng chuỗi tách. Tuy nhiên, bạn có thể tạo một hàm do người dùng xác định cho việc này, chẳng hạn như hàm được mô tả trong bài viết sau:

Với chức năng đó:

DELIMITER $$

CREATE FUNCTION SPLIT_STR(
  x VARCHAR(255),
  delim VARCHAR(12),
  pos INT
)
RETURNS VARCHAR(255) DETERMINISTIC
BEGIN 
    RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
       LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
       delim, '');
END$$

DELIMITER ;

bạn sẽ có thể xây dựng truy vấn của mình như sau:

SELECT SPLIT_STR(membername, ' ', 1) as memberfirst,
       SPLIT_STR(membername, ' ', 2) as memberlast
FROM   users;

Nếu bạn không muốn sử dụng chức năng do người dùng xác định và bạn không ngại truy vấn dài dòng hơn một chút, bạn cũng có thể thực hiện các thao tác sau:

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(membername, ' ', 1), ' ', -1) as memberfirst,
       SUBSTRING_INDEX(SUBSTRING_INDEX(membername, ' ', 2), ' ', -1) as memberlast
FROM   users;

Giải pháp tuyệt vời cho vấn đề này!
Bergkamp

bạn vẫn không thể sử dụng IN làm "mảng giá trị" từ thao tác phân tách đó chứ?
Miguel

3
Việc bạn sử dụng LENGTHmultibyte có an toàn không? "LENGTH (str): Trả về độ dài của chuỗi str, được đo bằng byte. Một ký tự đa chuỗi được tính là nhiều byte. Điều này có nghĩa là đối với một chuỗi chứa năm ký tự 2 byte, LENGTH () trả về 10, trong khi CHAR_LENGTH () trả về 5. "
Erk

Điều này sẽ không hoạt động đúng khi xử lý các ký tự multibyte / utf8, như @Erk đã đề cập. Chỉ có giải pháp đơn giản với hai câu lệnh SUBSTRING_INDEX hoạt động với utf8 / multibyte
Michael

LENGTH (), LOCATE () hoặc bất cứ thứ gì dựa vào số lượng vị trí sẽ thất bại với các ký tự đa nhân.
Michael

68

Biến thể CHỌN (không tạo chức năng do người dùng xác định):

SELECT IF(
        LOCATE(' ', `membername`) > 0,
        SUBSTRING(`membername`, 1, LOCATE(' ', `membername`) - 1),
        `membername`
    ) AS memberfirst,
    IF(
        LOCATE(' ', `membername`) > 0,
        SUBSTRING(`membername`, LOCATE(' ', `membername`) + 1),
        NULL
    ) AS memberlast
FROM `user`;

Cách tiếp cận này cũng quan tâm đến:

  • giá trị thành viên không có khoảng trắng : nó sẽ thêm toàn bộ chuỗi vào thành viên đầu tiên và đặt thành viên thành NULL.
  • MemberName giá trị mà có nhiều không gian : nó sẽ thêm tất cả mọi thứ trước khi vũ trụ đầu tiên memberfirst và phần còn lại (bao gồm cả không gian bổ sung) để memberlast.

Phiên bản CẬP NHẬT sẽ là:

UPDATE `user` SET
    `memberfirst` = IF(
        LOCATE(' ', `membername`) > 0,
        SUBSTRING(`membername`, 1, LOCATE(' ', `membername`) - 1),
        `membername`
    ),
    `memberlast` = IF(
        LOCATE(' ', `membername`) > 0,
        SUBSTRING(`membername`, LOCATE(' ', `membername`) + 1),
        NULL
    );

Cũng hữu ích là xem cách cắt bỏ từ cuối cùng của tên cuối cùng và tất cả những từ không phải cuối cùng cho tên, ví dụ: Mary A. Smith là loại tôi phải xử lý trong bảng db cũ sửa chữa. Tôi sẽ xem liệu tôi có thể tìm ra nó và đăng kết quả không, nếu không, nếu bạn cũng có thể đăng tùy chọn đó sẽ làm cho câu trả lời của bạn hoàn tất.
Lizardx

làm thế nào chúng ta có thể chuyển nó thành số nguyên vì tên thành viên là varchar .. hãy để thành viên đầu tiên thuộc kiểu int. Nó sẽ hoạt động nếu tôi trực tiếp sử dụng cast ()?
vô cực

Bạn xứng đáng với một huy chương.
rpajaziti

23

Có vẻ như các câu trả lời hiện có quá phức tạp hoặc không phải là một câu trả lời nghiêm ngặt cho câu hỏi cụ thể.

Tôi nghĩ rằng, câu trả lời đơn giản là truy vấn sau đây:

SELECT
    SUBSTRING_INDEX(`membername`, ' ', 1) AS `memberfirst`,
    SUBSTRING_INDEX(`membername`, ' ', -1) AS `memberlast`
;

Tôi nghĩ rằng không cần thiết phải xử lý các tên nhiều hơn hai từ trong tình huống cụ thể này. Nếu bạn muốn làm điều đó đúng cách, việc chia tách có thể rất khó khăn hoặc thậm chí là không thể trong một số trường hợp:

  • Johann Sebastian Bach
  • Johann Wolfgang von Goethe
  • Edgar Allan Poe
  • Jakob Ludwig Felix Mendelssohn-Bartkeepy
  • Petőfi Sándor
  • 澤黒

Trong một cơ sở dữ liệu được thiết kế đúng, tên người nên được lưu trữ cả phần và toàn bộ. Điều này không phải lúc nào cũng có thể, tất nhiên.


20

Nếu kế hoạch của bạn là thực hiện điều này như một phần của truy vấn, vui lòng không thực hiện điều đó (a) . Nghiêm túc mà nói, đó là một kẻ giết người hiệu suất. Có thể có những tình huống bạn không quan tâm đến hiệu suất (chẳng hạn như các công việc di chuyển một lần để phân chia các trường cho phép hiệu suất tốt hơn trong tương lai), nhưng, nếu bạn làm điều này thường xuyên cho bất kỳ thứ gì ngoài cơ sở dữ liệu chuột mickey, bạn Đang lãng phí tài nguyên.

Nếu bạn từng thấy mình phải xử lý chỉ là một phần của một cột trong một cách nào đó, thiết kế DB của bạn là thiếu sót. Nó có thể hoạt động tốt trên sổ địa chỉ nhà hoặc ứng dụng công thức hoặc bất kỳ cơ sở dữ liệu nhỏ nào khác nhưng nó sẽ không thể mở rộng cho các hệ thống "thực".

Lưu trữ các thành phần của tên trong các cột riêng biệt. Việc kết hợp các cột cùng với một cách ghép đơn giản (khi bạn cần tên đầy đủ) gần như luôn luôn là nhanh hơn nhiều so với việc tìm kiếm chúng tách biệt với tìm kiếm ký tự.

Nếu, vì một số lý do, bạn không thể tách trường, ít nhất là đặt vào các cột bổ sung và sử dụng trình kích hoạt chèn / cập nhật để điền vào chúng. Mặc dù không phải 3NF, nhưng điều này sẽ đảm bảo rằng dữ liệu vẫn nhất quán và sẽ tăng tốc độ truy vấn của bạn một cách ồ ạt. Bạn cũng có thể đảm bảo rằng các cột bổ sung được đặt thấp hơn (và được lập chỉ mục nếu bạn đang tìm kiếm trên chúng) cùng một lúc để không phải loay hoay với các vấn đề trường hợp.

Và, nếu bạn thậm chí không thể thêm các cột và trình kích hoạt, hãy lưu ý (và làm cho khách hàng của bạn biết, nếu đó là cho khách hàng) rằng nó không thể mở rộng được.


(a) Tất nhiên, nếu ý định của bạn là sử dụng truy vấn này để sửa lược đồ sao cho các tên được đặt vào các cột riêng biệt trong bảng thay vì truy vấn, tôi sẽ coi đó là cách sử dụng hợp lệ. Nhưng tôi nhắc lại, thực hiện nó trong truy vấn không thực sự là một ý tưởng tốt.


4
Đôi khi, bạn phải làm điều đó. Fe Tôi cần nó trong một kịch bản di chuyển, vì vậy tôi không quan tâm đến các buổi biểu diễn.
Matthieu Napoli

@dfmiller, vâng, tôi đã làm, do đó tôi đã trả lời hợp lý và chi tiết, và cảm ơn sự quan tâm của bạn. Nếu bạn có một vấn đề cụ thể với điều gì đó tôi đã viết, hãy chỉ ra và tôi sẽ xem liệu nó có thể được cải thiện không. Nhận xét hiện tại của bạn là khá nhiều vô ích trong việc cải thiện tình hình, nếu đó thực sự là ý định của bạn. Hoặc có thể bạn chỉ thích phun ra các bình luận ngẫu nhiên trên mạng, thật khó để nói :-) Tôi đứng trước câu trả lời, tất nhiên, truy cập cột phụ không thể mở rộng và hầu như luôn là một ý tưởng tồi, trừ khi nó được sử dụng cho mục đích thực sự sửa chữa truy cập cột phụ.
paxdiablo

3
Câu hỏi là làm thế nào để tách cột đơn thành 2 và sau đó bạn trả lời bằng cách nói "Đừng làm vậy" và sau đó tiến hành giải thích lý do tại sao chúng nên được chia. Đoạn đầu tiên của bạn có vẻ như bạn đang tranh luận ủng hộ hoặc giữ chúng như một cột, nhưng các đoạn khác lại nói ngược lại.
dfmiller

@dfmiller, có lẽ tôi đã hiểu nhầm câu hỏi, bây giờ tôi không chắc việc phân tách sẽ được thực hiện trong truy vấn hay bảng. Tôi đã làm rõ câu trả lời để hy vọng làm cho nó rõ ràng hơn.
paxdiablo

Tốt hơn nhiều. Tôi không bao giờ xem xét sử dụng một truy vấn chọn ngoại trừ để cập nhật cơ sở dữ liệu. Đó sẽ là một ý tưởng khủng khiếp.
dfmiller

7

dùng cái này

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX( `membername` , ' ', 2 ),' ',1) AS b, 
SUBSTRING_INDEX(SUBSTRING_INDEX( `membername` , ' ', -1 ),' ',2) AS c FROM `users` WHERE `userid`='1'

Điều này sẽ lấy chuỗi con được phân tách không gian đầu tiên và cuối cùng từ trường, không hoạt động trong mọi trường hợp. Ví dụ: nếu trường tên là "Lilly von Schtupp", thì bạn sẽ lấy 'Lilly', 'Schtupp' làm tên đầu tiên, họ.
John Franklin

5

Không trả lời chính xác câu hỏi, nhưng đối mặt với cùng một vấn đề tôi đã kết thúc việc này:

UPDATE people_exit SET last_name = SUBSTRING_INDEX(fullname,' ',-1)
UPDATE people_exit SET middle_name = TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(fullname,last_name,1),' ',-2))
UPDATE people_exit SET middle_name = '' WHERE CHAR_LENGTH(middle_name)>3 
UPDATE people_exit SET first_name = SUBSTRING_INDEX(fullname,concat(middle_name,' ',last_name),1)
UPDATE people_exit SET first_name = middle_name WHERE first_name = ''
UPDATE people_exit SET middle_name = '' WHERE first_name = middle_name

4

Trong MySQL, nó hoạt động tùy chọn này:

SELECT Substring(nameandsurname, 1, Locate(' ', nameandsurname) - 1) AS 
       firstname, 
       Substring(nameandsurname, Locate(' ', nameandsurname) + 1)    AS lastname 
FROM   emp  

cho phần còn lại của chuỗi vào trường thứ hai
M. Faraz

3

Trường hợp duy nhất mà bạn có thể muốn một chức năng như vậy là một truy vấn CẬP NHẬT sẽ thay đổi bảng của bạn để lưu Firstname và Lastname vào các trường riêng biệt.

Thiết kế cơ sở dữ liệu phải tuân theo các quy tắc nhất định và Chuẩn hóa cơ sở dữ liệu là một trong những quy tắc quan trọng nhất


Bình luận không cần thiết vì đây là chính xác những gì người đăng yêu cầu; cũng không chính xác vì có một triệu lần bạn có thể cần phải tách một chuỗi để chuẩn hóa tốt nhất. Không chắc chắn tại sao hoặc làm thế nào điều này đã được bỏ phiếu.
daticon

Sử dụng các chỉ mục trên các trường phân tách là không thể như biến MySQL thành một mulcher lá, nhưng điều đó sẽ không ngăn mọi người hỏi về nó. Câu trả lời tốt - cơ sở dữ liệu NÊN phản ánh dữ liệu, không phải thông số kỹ thuật lá của bạn.
Hold OfferHunger

2

Tôi đã có một cột trong đó cả tên và họ đều nằm trong một cột. Tên và họ được phân tách bằng dấu phẩy. Các mã dưới đây làm việc. KHÔNG có kiểm tra / sửa lỗi. Chỉ là một sự chia rẽ ngu ngốc. Đã sử dụng phpMyAdmin để thực thi câu lệnh SQL.

UPDATE tblAuthorList SET AuthorFirst = SUBSTRING_INDEX(AuthorLast,',',-1) , AuthorLast = SUBSTRING_INDEX(AuthorLast,',',1);

13.2.10 Cú pháp CẬP NHẬT


1

Điều này cần smhg từ đây và giới thiệu từ chỉ mục cuối cùng của một chuỗi con nhất định trong MySQL và kết hợp chúng. Đây là cho mysql, tất cả những gì tôi cần là phân chia tên thành First_name last_name với tên cuối cùng là một từ duy nhất, tên đầu tiên mọi thứ trước từ đó, trong đó tên có thể là null, 1 từ, 2 từ hoặc hơn 2 từ. Tức là: Không; Đức Maria; Mary Smith; Mary A. Smith; Mary Sue Ellen Smith;

Vì vậy, nếu tên là một từ hoặc null, last_name là null. Nếu tên là> 1 từ, last_name là từ cuối cùng và First_name tất cả các từ trước từ cuối cùng.

Lưu ý rằng tôi đã cắt bớt những thứ như Joe Smith Jr .; Joe Smith Esq. và v.v., bằng tay, điều này thật đau đớn, tất nhiên, nhưng nó đủ nhỏ để làm điều đó, vì vậy bạn muốn đảm bảo thực sự xem dữ liệu trong trường tên trước khi quyết định sử dụng phương pháp nào.

Lưu ý rằng điều này cũng cắt bớt kết quả, vì vậy bạn không kết thúc với khoảng trắng ở phía trước hoặc sau tên.

Tôi chỉ đăng bài này cho những người khác có thể google theo cách của họ ở đây để tìm kiếm những gì tôi cần. Điều này hoạt động, tất nhiên, kiểm tra nó với lựa chọn đầu tiên.

Đó là một lần duy nhất, vì vậy tôi không quan tâm đến hiệu quả.

SELECT TRIM( 
    IF(
        LOCATE(' ', `name`) > 0,
        LEFT(`name`, LENGTH(`name`) - LOCATE(' ', REVERSE(`name`))),
        `name`
    ) 
) AS first_name,
TRIM( 
    IF(
        LOCATE(' ', `name`) > 0,
        SUBSTRING_INDEX(`name`, ' ', -1) ,
        NULL
    ) 
) AS last_name
FROM `users`;


UPDATE `users` SET
`first_name` = TRIM( 
    IF(
        LOCATE(' ', `name`) > 0,
        LEFT(`name`, LENGTH(`name`) - LOCATE(' ', REVERSE(`name`))),
        `name`
    ) 
),
`last_name` = TRIM( 
    IF(
        LOCATE(' ', `name`) > 0,
        SUBSTRING_INDEX(`name`, ' ', -1) ,
        NULL
    ) 
);

0

Phương thức tôi đã sử dụng để phân chia First_name thành First_name và last_name khi dữ liệu đến tất cả trong trường First_name. Điều này sẽ chỉ đặt từ cuối cùng trong trường tên cuối cùng, vì vậy "john phillips sousa" sẽ là "john phillips" tên và "sousa" họ. Nó cũng tránh ghi đè bất kỳ hồ sơ đã được sửa chữa.

set last_name=trim(SUBSTRING_INDEX(first_name, ' ', -1)), first_name=trim(SUBSTRING(first_name,1,length(first_name) - length(SUBSTRING_INDEX(first_name, ' ', -1)))) where list_id='$List_ID' and length(first_name)>0 and length(trim(last_name))=0

0
UPDATE `salary_generation_tbl` SET
    `modified_by` = IF(
        LOCATE('$', `other_salary_string`) > 0,
        SUBSTRING(`other_salary_string`, 1, LOCATE('$', `other_salary_string`) - 1),
        `other_salary_string`
    ),
    `other_salary` = IF(
        LOCATE('$', `other_salary_string`) > 0,
        SUBSTRING(`other_salary_string`, LOCATE('$', `other_salary_string`) + 1),
        NULL
    );

-3

mysql 5.4 cung cấp một hàm phân chia riêng:

SPLIT_STR(<column>, '<delimiter>', <index>)

1
Bạn có thể cung cấp một liên kết đến các tài liệu. Một tìm kiếm của dev.mysql.com xuất hiện khô. Mục 12.5 không có đề xuất cộng đồng trong các ý kiến ​​cho chức năng này.
DRaehal
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.