Değeri bir alandan ikiye böl


125

Kullanıcıların membernamehem soyadını hem de ilk adını içeren bir tablo alanım var . O 2 alanlara olanlar bölmek mümkün mü memberfirst, memberlast?

Tüm kayıtlar bu "Ad Soyad" biçimine sahiptir (tırnak işaretleri ve aralarında boşluk olmadan).


6
"Tüm kayıtlar şu formata sahiptir:" Ad Soyad "(tırnak işaretleri ve aralarında boşluk olmadan)." ... mucizevi ... Lütfen lütfen veritabanı kararlar alırken, benim gibi insanlar hakkında unutma. Çoğu zaman, soyadımın yasadışı (sic) bir karakter içerdiğini söyleyen web siteleri görüyorum ... :(
Stijn de Witt

@StijndeWitt Genel olarak haklısınız, ancak görünen o ki bu veritabanı, en azından resmi formunda adınızı içermiyor. Ülkemde önce soyadlar yazılır, bu nedenle bu veri tablosunda da "ayrımcılık" yapılmalı. Sadece bunu gör ->
Dávid Horváth

Yanıtlar:


226

Maalesef MySQL'de bölünmüş dizge işlevi yoktur. Ancak bunun için aşağıdaki makalede açıklanan gibi kullanıcı tanımlı bir işlev oluşturabilirsiniz :

Bu işlevle:

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 ;

Sorgunuzu aşağıdaki gibi oluşturabilirsiniz:

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

Kullanıcı tanımlı bir işlevi kullanmamayı tercih ediyorsanız ve sorgunun biraz daha ayrıntılı olmasını istemiyorsanız, aşağıdakileri de yapabilirsiniz:

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

Bu problem için harika bir çözüm!
Bergkamp

yine de bu bölme işleminden bir "değerler dizisi" olarak IN'i kullanamazsınız?
Miguel

3
LENGTHÇok bayt kullanımınız güvenli mi? "LENGTH (str): Dizenin bayt cinsinden ölçülen uzunluğunu döndürür. Çok baytlı bir karakter birden çok bayt olarak sayılır. Bu, 2 baytlık beş karakter içeren bir dize için LENGTH () 10, CHAR_LENGTH () ise 5."
Erk

@Erk'in de belirttiği gibi, çok baytlı / utf8 karakterleriyle çalışırken bu düzgün çalışmayacaktır. Yalnızca iki SUBSTRING_INDEX deyimiyle basit çözüm utf8 / multibyte ile çalışır
Michael

LENGTH (), LOCATE () veya bir konum sayımına dayanan herhangi bir şey çok baytlı karakterlerle başarısız olur.
Michael

68

SEÇİM varyantı (kullanıcı tanımlı bir işlev oluşturmaz):

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

Bu yaklaşım aynı zamanda şunlarla da ilgilenir:

  • boşluksuz üye adı değerleri : tüm dizeyi memberfirst'e ekler ve memberlast'ı NULL olarak ayarlar.
  • Birden fazla boşluğa sahip üye adı değerleri : ilk alandan önceki her şeyi memberfirst'e ve kalanı (ek boşluklar dahil) memberlast'a ekler.

GÜNCELLEME sürümü şöyle olacaktır:

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

Soyadı için sadece son kelimeyi ve ad için son olmayanları nasıl keseceğimi görmek de yararlı olacaktır, örneğin: Mary A. Smith, eski bir db tablosunda bununla uğraşmam gereken türler düzeltme. Bakacağım, anlayıp sonucu gönderebilir miyim, yoksa, o seçeneği de gönderebilirseniz, cevabınızı tamamlarsınız.
Lizardx

üye adı varchar olduğundan tamsayıya nasıl çevirebiliriz .. üyefirst int türünde olsun. Doğrudan cast () kullanırsam çalışacak mı?
infinitywarior

Madalyayı hak ediyorsunuz bayım.
rpajaziti

23

Görünüşe göre mevcut yanıtlar aşırı karmaşıktır veya belirli soruya kesin bir yanıt değildir.

Bence basit cevap şu sorgu:

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

Bu özel durumda ikiden fazla kelimeyle uğraşmanın gerekli olmadığını düşünüyorum. Düzgün yapmak istiyorsanız, bölme işlemi bazı durumlarda çok zor hatta imkansız olabilir:

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

Düzgün tasarlanmış bir veri tabanında, insan isimleri hem parça parça hem de bütün olarak saklanmalıdır. Elbette bu her zaman mümkün değildir.


20

Planınız bunu bir sorgunun parçası olarak yapmaksa, lütfen bunu yapmayın (a) . Cidden, bu bir performans katilidir. Performansı önemsemediğiniz durumlar olabilir (gelecekte daha iyi performans sağlamak için alanları bölmek için tek seferlik geçiş işleri gibi), ancak bunu düzenli olarak mickey-mouse veritabanı dışında herhangi bir şey için yapıyorsanız, kaynakları boşa harcıyoruz.

Eğer varsa hiç kendinizi bir şekilde bir sütun yalnızca bir kısmını işlemek zorunda bulmak, sizin DB tasarım kusurlu. Bir ev adres defterinde veya tarif uygulamasında veya sayısız diğer küçük veritabanlarının herhangi birinde iyi çalışabilir, ancak "gerçek" sistemlere ölçeklenemeyecektir.

Adın bileşenlerini ayrı sütunlarda saklayın. Sütunları basit bir birleştirme ile birleştirmek (tam isme ihtiyacınız olduğunda), onları bir karakter aramasıyla ayırmaktan neredeyse her zaman çok daha hızlıdır.

Herhangi bir nedenle alanı bölemezseniz, en azından fazladan sütunları yerleştirin ve bunları doldurmak için bir ekleme / güncelleme tetikleyicisi kullanın. 3NF olmasa da, bu verilerin hala tutarlı olmasını garanti edecek ve sorgularınızı büyük ölçüde hızlandıracaktır. Ayrıca, vaka sorunlarıyla uğraşmak zorunda kalmamak için fazladan sütunların aynı anda daha küçük harfli olduğundan (ve üzerlerinde arama yapıyorsanız dizine eklendiğinden) emin olabilirsiniz.

Sütunları ve tetikleyicileri bile ekleyemiyorsanız, bunun ölçeklenebilir olmadığının farkında olun (ve bir müşteri için ise müşterinizi haberdar edin).


(a) Elbette, amacınız şemayı düzeltmek için bu sorguyu kullanmaksa , böylece adlar sorgu yerine tablodaki ayrı sütunlara yerleştirilirse , bunun geçerli bir kullanım olduğunu düşünürdüm. Ama yineliyorum, bunu sorguda yapmak gerçekten iyi bir fikir değil.


4
Bazen yapmak zorundasın. Fe Ona bir geçiş senaryosunda ihtiyacım var, bu yüzden performansları umursamıyorum.
Matthieu Napoli

@dfmiller, evet, yaptım, dolayısıyla gerekçeli ve detaylı cevabım ve ilginiz için teşekkürler. Yazdığım bir şeyle ilgili belirli bir sorununuz varsa , bunu belirtin ve ben de iyileştirilip iyileştirilemeyeceğini görelim. Şimdiki yorumunuz, eğer gerçekten niyetiniz buysa, durumu iyileştirmek için oldukça faydasız. Ya da belki sadece internette rastgele yorumlar söylemekten hoşlanıyorsunuz , bunu söylemek zor :-) Cevabın yanındayım, tabii ki, alt-sütun erişimi ölçeklenebilir değildir ve amacı için kullanılmadığı sürece neredeyse her zaman kötü bir fikirdir. aslında sabitleme alt sütunlu giriş.
paxdiablo

3
Soru, tek sütunu nasıl ikiye böleceğiniz ve ardından "Buna yapmayın" diyerek yanıt vermeniz ve ardından neden bölünmeleri gerektiğini açıklamaya devam etmenizdir. İlk paragrafınız lehinde tartışıyor veya bir sütun olarak tutuyormuşsunuz gibi geliyor, ancak diğer paragraflar bunun tersini söylüyor.
dfmiller

@dfmiller, belki soruyu yanlış anladım, şimdi ayırmanın sorguda mı yoksa tabloda mı yapılacağından emin değilim. Daha açık hale getirmek için cevabı netleştirdim.
paxdiablo

Çok daha iyi. Veritabanını güncellemek dışında hiçbir zaman seçme sorgusu kullanmayı düşünmedim. Bu korkunç bir fikir olurdu.
dfmiller

7

bunu kullan

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

Bu, her koşulda çalışmayan alandan ilk ve son boşlukla ayrılmış alt dizeyi alır. Örneğin, ad alanı "Lilly von Schtupp" ise ad, soyad olarak "Lilly", "Schtupp" alırsınız.
John Franklin

5

Soruyu tam olarak cevaplamıyorum, ancak aynı sorunla karşılaştım, bunu yaptım:

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

MySQL'de bu seçenek şu şekilde çalışıyor:

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

ikinci alana dize geri kalanını çekmek için
M. Faraz

3

Böyle bir işlevi isteyebileceğiniz tek durum, tablonuzu Ad ve Soyadı ayrı alanlarda depolayacak şekilde değiştirecek bir GÜNCELLEME sorgusudur.

Veritabanı tasarımı belirli kurallara uymalıdır ve Veritabanı Normalleştirme en önemlileri arasındadır


Gereksiz yorum, çünkü bu tam olarak posterin istediği şeydi; en iyi normalleştirme için bir dizeyi ayırmanız gerekebileceği milyon kez olduğu için de yanlıştır. Bunun neden veya nasıl oylandığından emin değilim.
daticon

Bölünmüş alanlarda dizin kullanmak, MySQL'i yaprak parçalayıcıya dönüştürmek kadar imkansızdır, ancak bu, insanları sormaktan alıkoymayacaktır. İyi cevap - veritabanı, yaprak parçalayıcı spesifikasyonlarınızı değil, verileri yansıtmalıdır.
HoldOffHunger

2

Hem adının hem de soyadının bir sütunda olduğu bir köşem vardı. Adı ve soyadı virgülle ayrılmıştır. Aşağıdaki kod işe yaradı. Hata kontrolü / düzeltmesi YOKTUR. Sadece aptalca bir bölünme. SQL deyimini yürütmek için phpMyAdmin kullanıldı.

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

13.2.10 UPDATE Sözdizimi


1

Bu, smhg'yi buradan alır ve Curtis'in MySQL'deki belirli bir alt dizenin Son dizininden alınır ve bunları birleştirir. Bu mysql için, tek ihtiyacım olan adın soyadı tek bir kelime ile adın soyadına düzgün bir şekilde bölünmesi, o tek kelimeden önceki her şeyin ilk adı, adın boş, 1 kelime, 2 kelime veya 2 kelimeden fazla. Yani: Boş; Mary; Mary Smith; Mary A. Smith; Mary Sue Ellen Smith;

Dolayısıyla, ad bir sözcük veya boşsa, soyadı boştur. Ad> 1 kelime ise, soyadı son kelimedir ve ad son kelimeden önceki tüm kelimelerdir.

Joe Smith Jr. gibi şeyleri zaten kırptığımı unutmayın; Joe Smith Esq. ve benzeri, elle, elbette acı vericiydi, ancak bunu yapacak kadar küçüktü, bu nedenle, hangi yöntemi kullanacağınıza karar vermeden önce ad alanındaki verilere gerçekten baktığınızdan emin olmak istersiniz.

Bunun aynı zamanda sonucu da kırptığını unutmayın, böylece adların önünde veya arkasında boşluk bırakmazsınız.

Bunu sadece ihtiyacım olanı arayarak burada google'da bulabilecek diğerlerine gönderiyorum. Bu çalışır, elbette, önce select ile test edin.

Tek seferlik bir şey, bu yüzden verimlilik umurumda değil.

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

Yöntem Verilerin tümü ilk_ad alanına ulaştığında, ilk_adı ilk_ad ve soyad olarak ayırırdım. Bu, soyad alanına yalnızca son kelimeyi koyacaktır, bu nedenle "john phillips sousa", "john phillips" adı ve "sousa" soyadı olacaktır. Ayrıca zaten düzeltilmiş kayıtların üzerine yazılmasını da önler.

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 yerel bir bölme işlevi sağlar:

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

1
Belgelere bir bağlantı verebilir misiniz? Dev.mysql.com araması sonuçsuz kalıyor. Bölüm 12.5'in bu işleve yönelik yorumlarda bir topluluk önerisi var.
DRaehal
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.