sql >> データベース >  >> RDS >> Mysql

純粋なmysqlでこれを解決できますか? (列の''で区切られた値に参加します)

    user_resourcesの場合 (t1)は、user => resourceごとに1行の「正規化されたテーブル」でした 組み合わせて、答えを取得するためのクエリは、joiningと同じくらい簡単です。 テーブルを一緒に。

    残念ながら、denormalized resourcesを持つことによって 列として:'リソースIDのリスト'';'で区切られますキャラクター。

    'resources'列を行に変換できれば、テーブルの結合が単純になるため、多くの問題が解消されます。

    要求された出力を生成するためのクエリ:

    SELECT user_resource.user, 
           resource.data
    
    FROM user_resource 
         JOIN integerseries AS isequence 
           ON isequence.id <= COUNT_IN_SET(user_resource.resources, ';') /* normalize */
    
         JOIN resource 
           ON resource.id = VALUE_IN_SET(user_resource.resources, ';', isequence.id)      
    ORDER BY
           user_resource.user,  resource.data
    

    出力:

    user        data    
    ----------  --------
    sampleuser  abcde   
    sampleuser  azerty  
    sampleuser  qwerty  
    stacky      qwerty  
    testuser    abcde   
    testuser    azerty  
    

    方法:

    「トリック」は、1からある制限までの数を含むテーブルを持つことです。私はそれをintegerseriesと呼んでいます 。これは、次のような「水平」のものを変換するために使用できます。';' delimited strings rowsに 。

    これが機能する方法は、integerseriesに「参加」するときです。 、cross joinを実行しています 、これは「内部結合」で「自然に」起こることです。

    各行は、integerseriesからの異なる「シーケンス番号」で複製されます そのrowに使用するリストの「リソース」の「インデックス」として使用するテーブル 。

    アイデアは次のとおりです。

    • リスト内のアイテムの数を数えます。
    • リスト内の位置に基づいて各アイテムを抽出します。
    • integerseriesを使用する 1つの行を一連の行に変換してuserから個々の「リソースID」を抽出します 。resources 進むにつれて。

    2つの関数を使用することにしました:

    • '区切り文字列リスト'と'インデックス'を指定した関数は、リスト内の位置の値を返します。私はそれを呼んでいます:VALUE_IN_SET 。つまり、「A; B; C」と「インデックス」が2の場合、「B」を返します。

    • '区切り文字列リスト'を指定した関数は、リスト内のアイテム数のカウントを返します。私はそれを呼んでいます:COUNT_IN_SET 。つまり、「A;B;C」を指定すると3が返されます

    これらの2つの関数とintegerseries delimited items list in a columnに対する一般的な解決策を提供する必要があります 。

    機能しますか?

    ';' delimited string in column 。 'cross_join'(isequence.idによって生成された値を含むすべての列が表示されます resources_indexとして ):

    SELECT user_resource.user, 
           user_resource.resources,
           COUNT_IN_SET(user_resource.resources, ';')                AS resources_count, 
           isequence.id                                              AS resources_index,
           VALUE_IN_SET(user_resource.resources, ';', isequence.id)  AS resources_value
    FROM 
         user_resource 
         JOIN  integerseries AS isequence 
           ON  isequence.id <= COUNT_IN_SET(user_resource.resources, ';')
    ORDER BY
           user_resource.user, isequence.id
    

    「正規化された」テーブル出力:

    user        resources  resources_count  resources_index  resources_value  
    ----------  ---------  ---------------  ---------------  -----------------
    sampleuser  1;2;3                    3                1  1                
    sampleuser  1;2;3                    3                2  2                
    sampleuser  1;2;3                    3                3  3                
    stacky      2                        1                1  2                
    testuser    1;3                      2                1  1                
    testuser    1;3                      2                2  3                
    

    上記の「正規化された」user_resourcesを使用する テーブルの場合、必要な出力を提供するための単純な結合です:

    必要な機能これらはどこでも使用できる一般的な関数です

    注:これらの関数の名前は、mysql FIND_IN_SET関数 。つまり、文字列リストに関して同様のことをしますか?

    COUNT_IN_SET 関数:character delimited itemsを返します 列に。

    DELIMITER $$
    
    DROP FUNCTION IF EXISTS `COUNT_IN_SET`$$
    
    CREATE FUNCTION `COUNT_IN_SET`(haystack VARCHAR(1024), 
                                   delim CHAR(1)
                                   ) RETURNS INTEGER
    BEGIN
          RETURN CHAR_LENGTH(haystack) - CHAR_LENGTH( REPLACE(haystack, delim, '')) + 1;
    END$$
    
    DELIMITER ;
    

    VALUE_IN_SET 関数:delimited listを処理します one based arrayとして 指定された「インデックス」の値を返します。

    DELIMITER $$
    
    DROP FUNCTION IF EXISTS `VALUE_IN_SET`$$
    
    CREATE FUNCTION `VALUE_IN_SET`(haystack VARCHAR(1024), 
                                   delim CHAR(1), 
                                   which INTEGER
                                   ) RETURNS VARCHAR(255) CHARSET utf8 COLLATE utf8_unicode_ci
    BEGIN
          RETURN  SUBSTRING_INDEX(SUBSTRING_INDEX(haystack, delim, which),
                         delim,
                         -1);
    END$$
    
    DELIMITER ;
    

    関連情報:

    • 最後に、SQLFiddle-動作するコード を取得する方法を考え出しました。 関数をコンパイルします。

    • SQLiteで機能するバージョンがあります データベースも SQLite-連結フィールドを正規化して結合しますか?

    テーブル(データ付き):

    CREATE TABLE `integerseries` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    /*Data for the table `integerseries` */
    
    insert  into `integerseries`(`id`) values (1);
    insert  into `integerseries`(`id`) values (2);
    insert  into `integerseries`(`id`) values (3);
    insert  into `integerseries`(`id`) values (4);
    insert  into `integerseries`(`id`) values (5);
    insert  into `integerseries`(`id`) values (6);
    insert  into `integerseries`(`id`) values (7);
    insert  into `integerseries`(`id`) values (8);
    insert  into `integerseries`(`id`) values (9);
    insert  into `integerseries`(`id`) values (10);
    

    リソース:

    CREATE TABLE `resource` (
      `id` int(11) NOT NULL,
      `data` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    /*Data for the table `resource` */
    
    insert  into `resource`(`id`,`data`) values (1,'abcde');
    insert  into `resource`(`id`,`data`) values (2,'qwerty');
    insert  into `resource`(`id`,`data`) values (3,'azerty');
    

    User_resource:

    CREATE TABLE `user_resource` (
      `user` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
      `resources` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL,
      PRIMARY KEY (`user`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    /*Data for the table `user_resource` */
    
    insert  into `user_resource`(`user`,`resources`) values ('sampleuser','1;2;3');
    insert  into `user_resource`(`user`,`resources`) values ('stacky','3');
    insert  into `user_resource`(`user`,`resources`) values ('testuser','1;3');
    


    1. OracleのINITCAP()関数

    2. スカラー集計を使用したインデックス付きビューのバグ

    3. SQLServerのビュー

    4. データベースをリセラーサーバーに移行する方法