1つのフィールドから2つのフィールドに値を分割する


125

membernameユーザーの姓と名の両方を含むテーブルフィールドがあります。2つのフィールドにそれらを分割することが可能ですかmemberfirstmemberlast

すべてのレコードは、「Firstname Lastname」という形式(引用符とその間のスペースなし)です。


6
"すべてのレコードは、この形式の" Firstname Lastname "(引用符とその間のスペースなし)です。" ...奇跡的に...、してくださいしてくださいデータベースの意思決定を行う際に、私のような人を忘れないでください。自分の姓に違法な(原文のままの)文字が含まれているというウェブサイトが頻繁に表示されます... :(
Stijn de Witt

@StijndeWittあなたは大体正しいですが、このデータベースにはあなたの名前が含まれていないようです。少なくとも正式な形式ではそうではありません。私の国では姓が最初に書かれているので、このデータ表でも私は「差別」されます。ただ、これを見る- >
デヴィッドHorvathの

回答:


226

残念ながら、MySQLは分割文字列関数を備えていません。ただし、次の記事で説明するようなユーザー定義関数を作成できます。

その機能で:

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 ;

次のようにクエリを作成できます。

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

ユーザー定義関数を使用しないことを希望し、クエリが少し冗長になることを気にしない場合は、次のことも実行できます。

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

この問題の素晴らしい解決策!
Bergkamp、2015年

それでも、INをその分割操作の「値の配列」として使用することはできませんか?
ミゲル

3
LENGTHマルチバイトの使用は 安全ですか?"LENGTH(str):文字列strの長さをバイトで返します。マルチバイト文字は複数バイトとしてカウントされます。これは、5つの2バイト文字を含む文字列の場合、LENGTH()は10を返し、CHAR_LENGTH()は5.」
エルク、2016年

@Erkが述べたように、これはマルチバイト/ utf8文字を処理するときに正しく機能しません。2つのSUBSTRING_INDEXステートメントを使用した単純なソリューションのみがutf8 /マルチバイトで機能します
Michael

LENGTH()、LOCATE()、または位置カウントに依存するものは、マルチバイト文字で失敗します。
マイケル

68

SELECTバリアント(ユーザー定義関数を作成しない):

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

このアプローチは、次の処理も行います。

  • スペースのない membername :文字列全体をmemberfirstに追加し、memberlastをNULLに設定します。
  • 複数のスペースがある membername :最初のスペースの前のすべてをmemberfirstに追加し、残り(追加のスペースを含む)をmemberlastに追加します。

UPDATEバージョンは次のようになります。

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

また、姓の最後の単語だけを切り捨てて、姓以外のすべての単語を切り捨てる方法を確認することも役立ちます。たとえば、次のようにします。修正。私がそれを理解して結果を投稿できるかどうかを確認します。できない場合は、そのオプションも投稿できれば、回答が完全になります。
Lizardx

membernameはvarcharなので、どのように整数にキャストできますか?memberfirstをint型にします。cast()を直接使用しても機能しますか?
インフィニティ

君はメダルに値する。
rpajaziti

23

既存の回答は複雑すぎるか、特定の質問に対する厳密な回答ではないようです。

簡単な答えは次のクエリだと思います。

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

この特定の状況では、2語を超える名前を処理する必要はないと思います。適切に実行したい場合、分割が非常に困難になったり、場合によっては不可能になったりします。

  • ヨハン・セバスチャン・バッハ
  • ヨハン・ヴォルフガング・フォン・ゲーテ
  • エドガーアランポー
  • ヤコブ・ルートヴィヒ・フェリックス・メンデルスゾーン・バルトルディ
  • ペトフィサンドール
  • 澤黒

適切に設計されたデータベースでは、人間の名前は部分的にも全体的にも格納する必要があります。もちろん、これは常に可能であるとは限りません。


20

あなたの計画はの一部としてこれを行うのであれば、クエリ、してくださいしていないことを行います() 。真剣に、それはパフォーマンスキラーです。パフォーマンスを気にしない場合もあります(フィールドを分割して将来のパフォーマンスを向上させるための1回限りの移行ジョブなど)が、ミッキーマウスデータベース以外に対して定期的にこれを行う場合は、リソースを無駄にしています。

あなたがいる場合、これまで自分が何らかの方法で列の一部だけを処理する必要が見つけ、あなたのDB設計は欠陥があります。自宅のアドレス帳やレシピアプリケーション、その他の無数の小さなデータベースで問題なく動作する可能性がありますが、「実際の」システムには拡張できません。

名前のコンポーネントを別々の列に格納します。文字検索で列を分割するよりも、列を単純な連結(完全な名前が必要な場合)で結合する方がほぼ常に高速です。

何らかの理由でフィールドを分割できない場合は、少なくとも追加の列を入力し、挿入/更新トリガーを使用してそれらを設定します。これは3NFではありませんが、データの一貫性が保証され、クエリが大幅に高速化されます。また、大文字と小文字の問題をいじる必要がないように、追加の列が同時に小文字である(そしてそれらを検索する場合はインデックスが付けられる)ことを確認することもできます。

また、列やトリガーを追加することさえできない場合は、スケーラブルではないことに注意してください(クライアントの場合は、クライアントに通知してください)。


(a)もちろん、このクエリを使用してスキーマを修正し、名前をクエリではなくテーブル内の別の列に配置する場合は、これを有効な使用法と見なします。繰り返しますが、クエリでそれを行うことは、実際には良い考えではありません。


4
時々、あなたはそれをしなければなりません。移行スクリプトで必要なので、パフォーマンスは気にしません。
Matthieu Napoli

@dfmiller、はい、私はそうしました、それゆえ私の合理的で詳細な応答、そしてあなたの興味に感謝します。私が書いたものに特定の問題がある場合は、指摘してください。改善できるかどうか確認します。あなたの現在のコメントは、それが本当にあなたの意図であったとしても、状況を改善する上でほとんど役に立ちません。または、ネット上でランダムなコメントを噴き出すのが好きなのかもしれませんが、言うのは難しいです:-)私は答えを待機しています。もちろん、サブカラムアクセスはスケーラブルではなく、目的に使用しない限り、ほとんどの場合悪い考えです。サブコラムのアクセスを実際に修正します。
paxdiablo 2014

3
問題は、単一の列を2つに分割する方法です。次に、「それにはいけない」と答えてから、なぜ分割する必要があるかを説明します。あなたの最初の段落はあなたが賛成またはそれらを1つの列として保持しているように主張しているように聞こえますが、他の段落は反対です。
dfmiller 2014

@dfmiller、おそらく私は質問を誤解しましたが、クエリまたはテーブルのどちらで分離が行われるべきかは今はわかりません。うまくいけばそれをより明確にするために答えを明確にしました。
paxdiablo 2014

ずっといい。データベースを更新する以外に選択クエリを使用することは考えていません。それはひどい考えでしょう。
dfmiller 14

7

これを使って

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

これは、フィールドから最初と最後のスペースで区切られた部分文字列を取得しますが、すべての状況で機能するわけではありません。たとえば、名前フィールドが「Lilly von Schtupp」の場合、「Lilly」、「Schtupp」が名、姓として表示されます。
ジョンフランクリン

5

質問に正確に答えるのではなく、同じ問題に直面して、私はこれをやった:

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では、このオプションが機能しています。

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

文字列の残りを2番目のフィールドに取り込むため
M.ファラス

3

このような関数が必要になる唯一のケースは、FirstnameとLastnameを別々のフィールドに格納するようにテーブルを変更するUPDATEクエリです。

データベースの設計は特定のルールに従う必要があり、データベースの正規化は最も重要なルールの1つです


これはポスターが要求したとおりの不必要なコメントです。また、最高の正規化のために文字列を分割する必要がある場合が100万回あるため、不正確です。なぜ、どのようにしてこれが投票されたのかは不明です。
daticon 2018年

分割フィールドでインデックスを使用することは、MySQLをマルチ機能にするのとほぼ同じくらい不可能ですが、それによって人々がそれについて尋ねることを止めることはありません。良い答え-リーフマルチャーの仕様ではなく、データベースがデータを反映する必要があります。
HoldOffHunger 2018

2

姓と名の両方が1つの列にある列がありました。姓名はコンマで区切られています。以下のコードは機能しました。エラーのチェック/修正はありません。ばかげた分割。phpMyAdminを使用してSQLステートメントを実行しました。

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

13.2.10 UPDATE構文


1

これは、smhgをここから、curt をMySQLの特定のサブストリングのLastインデックスから取得して、それらを結合します。これはmysqlの場合、必要なのは、名前をfirst_name last_nameに適切に分割して、姓を1つの単語、名前がその単一の単語の前にあるすべての名前にすることです。名前はnull、1単語、2単語、または2語以上。すなわち:ヌル。メアリー; メアリー・スミス; メアリーA.スミス; メアリースーエレンスミス;

したがって、nameが1つの単語またはnullの場合、last_nameはnullです。nameが1単語よりも大きい場合、last_nameは最後の単語、first_nameは最後の単語より前のすべての単語です。

Joe Smith Jr.のようなものはすでに削除していることに注意してください。Joe Smith Esq。もちろん、手作業などは大変でしたが、それを行うには十分に小さいため、使用する方法を決定する前に、名前フィールドのデータを実際に確認する必要があります。

これにより結果もトリミングされるため、名前の前後にスペースが含まれることはありません。

私はここに私が必要なものを探してグーグルするかもしれない他の人のためにこれを投稿しています。もちろん、これは機能します。最初にselectでテストしてください。

一度きりなので効率は気にしません。

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

データがすべてfirst_nameフィールドに到着したときに、first_nameをfirst_nameとlast_nameに分割する方法を使用しました。これは、姓のフィールドに最後の単語のみを入力するため、「john phillips sousa」は「john phillips」の名と「sousa」の姓になります。また、すでに修正されているレコードの上書きも回避します。

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はネイティブの分割関数を提供します:

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

1
ドキュメントへのリンクを提供できますか。dev.mysql.comの検索が空になります。セクション12.5には、この関数のコメントにコミュニティの提案があります。
DRaehal 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.