Pisahkan nilai dari satu bidang ke dua


125

Saya punya bidang tabel membernameyang berisi nama belakang dan nama pengguna. Apakah mungkin untuk membagi mereka menjadi 2 bidang memberfirst, memberlast?

Semua catatan memiliki format ini "Firstname Lastname" (tanpa tanda kutip dan spasi di antaranya).


6
"Semua catatan memiliki format ini" Firstname Lastname "(tanpa tanda kutip dan spasi di antaranya)." ... secara ajaib ... Tolong, tolong , jangan lupakan orang-orang seperti saya saat membuat keputusan basis data. Terlalu sering saya mendapatkan situs web yang memberi tahu saya nama belakang saya mengandung karakter ilegal (sic) ... :(
Stijn de Witt

@StijndeWitt Anda benar secara umum, namun tampaknya database ini tidak mengandung nama Anda, setidaknya tidak dalam bentuk resminya. Di negara saya nama keluarga ditulis terlebih dahulu, jadi saya juga akan "didiskriminasi" dalam tabel data ini. Lihat saja ini ->
Dávid Horváth

Jawaban:


226

Sayangnya MySQL tidak menampilkan fungsi string split. Namun Anda dapat membuat fungsi yang ditentukan pengguna untuk ini, seperti yang dijelaskan dalam artikel berikut:

Dengan fungsi itu:

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 ;

Anda dapat membuat kueri Anda sebagai berikut:

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

Jika Anda memilih untuk tidak menggunakan fungsi yang ditentukan pengguna dan Anda tidak keberatan kueri menjadi lebih bertele-tele, Anda juga dapat melakukan hal berikut:

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

Solusi Hebat untuk masalah ini!
Bergkamp

Anda masih tidak dapat menggunakan IN sebagai "array nilai" dari operasi pemisahan itu?
Miguel

3
Apakah penggunaan LENGTHmultibyte Anda aman? "LENGTH (str): Mengembalikan panjang string str, diukur dalam byte. Karakter multibyte dihitung sebagai beberapa byte. Ini berarti bahwa untuk string yang berisi lima karakter 2-byte, LENGTH () mengembalikan 10, sedangkan CHAR_LENGTH () mengembalikan 5. "
Erk

Ini tidak akan berfungsi dengan baik ketika berhadapan dengan karakter multibyte / utf8, seperti yang disebutkan @Erk. Hanya solusi sederhana dengan dua pernyataan SUBSTRING_INDEX yang berfungsi dengan utf8 / multibyte
Michael

LENGTH (), LOCATE () atau apa pun yang bergantung pada jumlah posisi akan gagal dengan karakter multibyte.
Michael

68

Varian SELECT (tidak membuat fungsi yang ditentukan pengguna):

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`;

Pendekatan ini juga menangani:

  • Membername nilai-nilai tanpa spasi : akan menambahkan seluruh string untuk memberfirst dan set memberlast ke NULL.
  • nilai membername yang memiliki banyak spasi : ini akan menambahkan segalanya sebelum spasi pertama ke memberfirst dan sisanya (termasuk spasi tambahan) ke memberlast.

Versi UPDATE adalah:

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
    );

Juga berguna untuk melihat bagaimana memotong hanya kata terakhir untuk nama belakang, dan semua yang tidak terakhir untuk nama depan, misalnya: Mary A. Smith yang merupakan tipe yang harus saya tangani dalam tabel db lama memperbaiki. Saya akan melihat apakah saya dapat mengetahuinya dan memposting hasilnya, jika tidak, jika Anda dapat memposting opsi itu juga yang akan membuat jawaban Anda lengkap.
Lizardx

bagaimana kita bisa melemparkannya ke integer karena nama anggota adalah varchar .. biarkan memberfir menjadi tipe int Apakah akan berfungsi jika saya langsung menggunakan cast ()?
infinitywarior

Anda layak mendapatkan medali.
rpajaziti

23

Tampaknya tanggapan yang ada terlalu rumit atau bukan jawaban yang ketat untuk pertanyaan tertentu.

Saya pikir, jawaban sederhana adalah pertanyaan berikut:

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

Saya pikir tidak perlu berurusan dengan lebih dari dua kata dalam situasi khusus ini. Jika Anda ingin melakukannya dengan benar, pemisahan bisa sangat sulit atau bahkan tidak mungkin dalam beberapa kasus:

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

Dalam database yang dirancang dengan baik, nama manusia harus disimpan baik di bagian maupun secara keseluruhan. Ini tidak selalu mungkin, tentu saja.


20

Jika rencana Anda melakukan ini sebagai bagian dari kueri, tolong jangan lakukan itu (a) . Serius, ini adalah pembunuh kinerja. Mungkin ada situasi di mana Anda tidak peduli dengan kinerja (seperti pekerjaan migrasi satu kali untuk membagi bidang yang memungkinkan kinerja yang lebih baik di masa depan) tetapi, jika Anda melakukan ini secara teratur untuk apa pun selain database mickey-mouse, Anda sedang membuang-buang sumber daya.

Jika Anda pernah menemukan diri Anda harus memproses hanya bagian dari kolom dalam beberapa cara, desain DB Anda cacat. Mungkin baik-baik saja pada buku alamat rumah atau aplikasi resep atau berbagai database kecil lainnya tetapi tidak akan terukur untuk sistem "nyata".

Simpan komponen nama di kolom terpisah. Ini hampir selalu jauh lebih cepat untuk bergabung dengan kolom bersama dengan rangkaian sederhana (ketika Anda membutuhkan nama lengkap) daripada membaginya dengan pencarian karakter.

Jika, karena alasan tertentu Anda tidak dapat membagi bidang, setidaknya masukkan kolom tambahan dan gunakan pemicu sisipan / perbarui untuk mengisinya. Meskipun bukan 3NF, ini akan menjamin bahwa data masih konsisten dan secara besar-besaran akan mempercepat kueri Anda. Anda juga dapat memastikan bahwa kolom tambahan berukuran lebih rendah (dan diindeks jika Anda mencari) pada saat yang sama sehingga tidak harus bermain-main dengan masalah kasus.

Dan, jika Anda bahkan tidak dapat menambahkan kolom dan pemicu, sadari (dan buat klien Anda sadar, jika itu untuk klien) bahwa itu tidak dapat diskalakan.


(a) Tentu saja, jika maksud Anda adalah menggunakan kueri ini untuk memperbaiki skema sehingga nama-nama ditempatkan ke dalam kolom terpisah dalam tabel daripada kueri, saya akan menganggapnya sebagai penggunaan yang valid. Tapi saya tegaskan, melakukannya dalam kueri sebenarnya bukan ide yang bagus.


4
Terkadang, Anda harus melakukannya. Karena saya membutuhkannya dalam skrip migrasi, jadi saya tidak peduli dengan kinerja.
Matthieu Napoli

@ PDFmiller, ya, saya lakukan, karenanya tanggapan saya beralasan dan terperinci, dan terima kasih atas minat Anda. Jika Anda memiliki masalah spesifik dengan sesuatu yang saya tulis, tunjukkan dan saya akan melihat apakah itu dapat diperbaiki. Komentar Anda saat ini sangat tidak berguna dalam memperbaiki situasi, jika itu memang niat Anda. Atau mungkin Anda hanya suka semburan komentar acak di internet, sulit untuk mengatakan :-) Saya setuju, tentu saja, akses sub-kolom tidak dapat diskalakan dan hampir selalu merupakan ide yang buruk, kecuali itu digunakan untuk tujuan sebenarnya memperbaiki akses sub-kolom.
paxdiablo

3
Pertanyaannya adalah bagaimana membagi satu kolom menjadi 2 dan kemudian Anda merespons dengan mengatakan "Jangan lakukan itu" dan kemudian lanjutkan untuk menjelaskan mengapa mereka harus dibagi. Paragraf pertama Anda sepertinya Anda mendukung atau mempertahankannya sebagai satu kolom, tetapi paragraf lain mengatakan sebaliknya.
dfmiller

@ PDFmiller, mungkin saya salah paham pertanyaannya, saya tidak yakin sekarang apakah pemisahan harus dilakukan dalam query atau tabel. Saya sudah mengklarifikasi jawabannya agar mudah-mudahan lebih jelas.
paxdiablo

Jauh lebih baik. Saya tidak pernah mempertimbangkan menggunakan kueri pemilihan kecuali memperbarui basis data. Itu akan menjadi ide yang mengerikan.
dfmiller

7

Gunakan ini

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

Ini akan mengambil substring yang dibatasi ruang pertama dan terakhir dari bidang, yang tidak berfungsi dalam semua keadaan. Misalnya, jika bidang nama adalah "Lilly von Schtupp", maka Anda akan mendapatkan 'Lilly', 'Schtupp' sebagai nama depan, nama keluarga.
John Franklin

5

Tidak persis menjawab pertanyaan, tetapi dihadapkan dengan masalah yang sama saya akhirnya melakukan ini:

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

Di MySQL ini berfungsi opsi ini:

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

untuk mengambil sisa string ke bidang kedua
M. Faraz

3

Satu-satunya kasus di mana Anda mungkin menginginkan fungsi tersebut adalah kueri UPDATE yang akan mengubah tabel Anda untuk menyimpan Nama Depan dan Nama Belakang ke dalam bidang yang terpisah.

Desain database harus mengikuti aturan tertentu, dan Normalisasi Database adalah yang paling penting


Komentar yang tidak perlu karena inilah yang diminta oleh poster; juga tidak akurat karena ada jutaan kali Anda mungkin perlu membagi string untuk normalisasi terbaik. Tidak yakin mengapa atau bagaimana ini bisa dipilih.
Daticon

Menggunakan indeks pada bidang terbagi hampir tidak mungkin seperti membuat MySQL menjadi mulsa daun, tetapi itu tidak akan menghentikan orang untuk bertanya tentang hal itu. Jawaban yang bagus - database HARUS mencerminkan data, bukan spesifikasi mulsa daun Anda.
HoldOffHunger

2

Saya memiliki kolom di mana nama depan dan belakang keduanya berada dalam satu kolom. Nama depan dan belakang dipisahkan oleh koma. Kode di bawah ini berfungsi. TIDAK ada pengecekan / koreksi kesalahan. Hanya sedikit bodoh. Digunakan phpMyAdmin untuk menjalankan pernyataan SQL.

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

13.2.10 Sintaks Pembaruan


1

Ini mengambil smhg dari sini dan singkat dari indeks Terakhir dari substring yang diberikan di MySQL dan menggabungkan mereka. Ini untuk mysql, yang saya butuhkan adalah untuk mendapatkan nama yang layak menjadi first_name last_name dengan nama belakang satu kata, nama pertama semuanya sebelum kata tunggal itu, di mana namanya bisa nol, 1 kata, 2 kata, atau lebih dari 2 kata. Yaitu: Null; Mary; Mary Smith; Mary A. Smith; Mary Sue Ellen Smith;

Jadi, jika nama adalah satu kata atau nol, nama belakang adalah nol. Jika nama adalah> 1 kata, last_name adalah kata terakhir, dan first_name semua kata sebelum kata terakhir.

Perhatikan bahwa saya sudah memangkas hal-hal seperti Joe Smith Jr.; Joe Smith Esq. dan seterusnya, secara manual, yang menyakitkan, tentu saja, tetapi cukup kecil untuk melakukan itu, jadi Anda ingin memastikan untuk benar-benar melihat data di bidang nama sebelum memutuskan metode mana yang akan digunakan.

Perhatikan bahwa ini juga memangkas hasilnya, jadi Anda tidak berakhir dengan spasi di depan atau setelah nama.

Saya hanya memposting ini untuk orang lain yang mungkin google jalan di sini mencari apa yang saya butuhkan. Ini berfungsi, tentu saja, mengujinya dengan pilih dulu.

Ini masalah satu kali, jadi saya tidak peduli efisiensi.

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

Metode yang saya gunakan untuk membagi first_name menjadi first_name dan last_name ketika data tiba semua di bidang first_name. Ini hanya akan menempatkan kata terakhir di bidang nama belakang, jadi "john phillips sousa" akan menjadi "john phillips" nama depan dan "sousa" nama belakang. Itu juga menghindari menimpa catatan yang sudah diperbaiki.

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 menyediakan fungsi pemisahan asli:

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

1
Bisakah Anda memberikan tautan ke dokumentasi. Pencarian dev.mysql.com menjadi kering. Bagian 12.5 memang memiliki saran komunitas dalam komentar untuk fungsi ini.
DRaehal
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.