SQLite-规范化连接字段并加入它?

时间:2015-12-11 19:56:26

标签: sql sqlite

我有一些数据存储在字段中以逗号分隔的值中,我想将这些以逗号分隔的值转换为临时表并使用它们连接到另一个表

CREATE TABLE STRATEGY (STRATEGY_ID INTEGER PRIMARY KEY, APPLIED_SET_IDS VARCHAR);

CREATE TABLE ACTION_SET (APPLIED_ACTION_SET_ID INTEGER PRIMARY KEY, VALUE VARCHAR);

+-----------+---------------+
|STRATEGY_ID|APPLIED_SET_IDS|
+-----------+---------------+
|          1|1,3,6,7        |
|          2|1,2,4          |

+---------------------+-----+
|APPLIED_ACTION_SET_ID|VALUE|
+---------------------+-----+
|                    1|X    |
|                    2|Y    |
|                    3|Z    |
|                    4|H    |
|                    5|I    |
|                    6|J    |
|                    7|K    |
|                    8|L    |

我知道我必须使用某种形式的递归as shown here。但是我所做的每一次尝试都让我头晕了一下。我的临时表也需要保留APPLIED_SET_ID值的原始连接顺序,就像这样......

+-----------+-----+--------------+
|STRATEGY_ID|ORDER|APPLIED_SET_ID|
+-----------+-----+--------------+
|          1|    1|             1|
|          1|    2|             3|
|          1|    3|             6|
|          1|    4|             7|
|          2|    1|             1|
|          2|    2|             2|
|          2|    3|             4|

最终,我将此表连接到第二个现有表,并使用GROUP_CONCAT以相同的顺序将ID替换为相应的值。

+-----------+------------------+
|STRATEGY_ID|APPLIED_SET_VALUES|
+-----------+------------------+
|          1|X,Z,J,K           |
|          2|X,Y,H             |

因为顺序,正则表达式已经出局(否则我可以将逗号转换为管道并加入REGEXP语句)。我怎样才能做到这一点?我知道这不是标准化的,但我需要使用这个当前的结构。感谢您提前提供的任何帮助。

2 个答案:

答案 0 :(得分:0)

可以从SQLite调用PHP函数。它可以使用简单的查询来规范化'桌子。

我已经转换了' normalize逗号分隔的字符串,其中包含按键'到SQLite。见:(joining on ';' separated values in a column) for a more complete explanation

我开始寻找将函数转换为在SQLite中运行的方法。经过一番搜索,我发现了这个:Working with PHP UDFs in SQLite

我觉得很有趣 - 从SQLite调用PHP函数。这听起来很有趣!

它可以工作,但你不能使用PDO。您必须直接使用SQLite函数。 Vendor Specific Database Extensions: SQLite3

更新了您的数据(有关其他问题的工作代码,请参阅之前的修改)

代码:

<?php // Q34231542 -- count_in_set, value_in_set

/*
 * See this question for a rather more complete explanation of what this is doing...
 *
 * https://stackoverflow.com/questions/33782728/can-i-resolve-this-with-pure-mysql-joining-on-separated-values-in-a-column/
 */


define('SQLITE_DB', __DIR__ .'/Q34231542.sqlite');

/**
 * @var SQLite3
 */
$db = new SQLite3(SQLITE_DB);

/*
 * Define the functions for use by SQLite.
 */
$db->createFunction('count_in_set', 'count_in_set', 2);
$db->createFunction('value_in_set', 'value_in_set', 3);


$sql ="
SELECT STRATEGY.STRATEGY_ID                         as 'applied_strategy_id',
       STRATEGY.APPLIED_SET_IDS                     as 'applied_strategy_list',
       isequence.id                                 as 'which_strategy',
       COUNT_IN_SET(STRATEGY.APPLIED_SET_IDS, ',')  as 'StrategyCount',
       VALUE_IN_SET(STRATEGY.APPLIED_SET_IDS, ',', isequence.id)
                                                    as 'TheStrategy',
       ACTION_SET.VALUE                             as 'Action_Set_Value'


FROM STRATEGY
     JOIN integerseries AS isequence
       ON isequence.id <= COUNT_IN_SET(STRATEGY.APPLIED_SET_IDS, ',') /* normalize */

     JOIN ACTION_SET
       ON ACTION_SET.APPLIED_ACTION_SET_ID = VALUE_IN_SET(STRATEGY.APPLIED_SET_IDS, ',', isequence.id)
ORDER BY
       STRATEGY.STRATEGY_ID , ACTION_SET.APPLIED_ACTION_SET_ID;
";

/*
 * Run the query
 */
$stmt   = $db->prepare($sql);
$result = $stmt->execute();

/*
 * Get the results
 */
$rows = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) { // fetch all the rows for now...
    $rows[] = $row;
}

/*
 * output...
 */
 // \Kint::dump($rows);
 echo '<pre>';
 var_dump($rows);
 echo '</pre>';

exit;

/* -------------------------------------------------------------------------
 *  The PHP functions called from SQLite
 */

/**
 * Count the number of delimited items in a string
 *
 * @param string $delimitedValues
 * @param string $delim
 * @return integer
 */
function count_in_set($delimitedValues, $delim)
{
    return substr_count(trim($delimitedValues, $delim), $delim) + 1;
}

/**
 * Treat the delimited values as ONE BASED array.
 *
 * @param string $delimitedValues
 * @param string $delim
 * @param integer $which
 * @return string
 */
function value_in_set($delimitedValues, $delim, $which)
{
    $items = explode($delim, $delimitedValues);
    return $items[$which - 1];
}

输出:

    applied_strategy_id applied_strategy_list which_strategy StrategyCount TheStrategy Action_Set_Value
#1  1                   "1,3,6,7"             1              4            "1"         "X"
#2  1                   "1,3,6,7"             2              4            "3"         "Z"
#3  1                   "1,3,6,7"             3              4            "6"         "J"
#4  1                   "1,3,6,7"             4              4            "7"         "K"
#5  2                   "1,2,4"               1              3            "1"         "X"
#6  2                   "1,2,4"               2              3            "2"         "Y"
#7  2                   "1,2,4"               3              3            "4"         "H"

数据:

CREATE TABLE [integerseries] (
  [id] INTEGER NOT NULL PRIMARY KEY);
INSERT INTO "integerseries" VALUES(1);
INSERT INTO "integerseries" VALUES(2);
INSERT INTO "integerseries" VALUES(3);
INSERT INTO "integerseries" VALUES(4);
INSERT INTO "integerseries" VALUES(5);
INSERT INTO "integerseries" VALUES(6);
INSERT INTO "integerseries" VALUES(7);
INSERT INTO "integerseries" VALUES(8);
INSERT INTO "integerseries" VALUES(9);
INSERT INTO "integerseries" VALUES(10);


CREATE TABLE STRATEGY (STRATEGY_ID INTEGER PRIMARY KEY, APPLIED_SET_IDS VARCHAR);
INSERT INTO "STRATEGY" VALUES(1,'1,3,6,7');
INSERT INTO "STRATEGY" VALUES(2,'1,2,4');

CREATE TABLE ACTION_SET (APPLIED_ACTION_SET_ID INTEGER PRIMARY KEY, VALUE VARCHAR);
INSERT INTO "ACTION_SET" VALUES(1,'X');
INSERT INTO "ACTION_SET" VALUES(2,'Y');
INSERT INTO "ACTION_SET" VALUES(3,'Z');
INSERT INTO "ACTION_SET" VALUES(4,'H');
INSERT INTO "ACTION_SET" VALUES(5,'I');
INSERT INTO "ACTION_SET" VALUES(6,'J');
INSERT INTO "ACTION_SET" VALUES(7,'K');
INSERT INTO "ACTION_SET" VALUES(8,'L');

答案 1 :(得分:0)

我的同事开发了一个非常聪明的解决方案,假设分隔符是管道|而不是逗号,

他使用REGEXPINSTR()函数来获取数字位置,该值驱动了排序。

SELECT STRATEGY_ID,
APPLIED_SET_IDS,
GROUP_CONCAT(VALUE,'|') as DESCRIPTION 

FROM ( 
    SELECT STRATEGY_ID,
    APPLIED_SET_IDS,
    CASE 
        WHEN APPLIED_ACTION_SET_ID = APPLIED_SET_IDS THEN 1
        WHEN instr(APPLIED_SET_IDS, APPLIED_ACTION_SET_ID || '|') = 1 Then 1
        WHEN instr(APPLIED_SET_IDS, '|' || APPLIED_ACTION_SET_ID || '|') > 0 Then instr(APPLIED_SET_IDS, '|' || APPLIED_ACTION_SET_ID || '|')
        ELSE 999999
    END AS APPLIED_ORDER,
    VALUE

    FROM STRATEGY 
    INNER JOIN ACTION_SET
    ON ACTION_SET.APPLIED_ACTION_SET_ID REGEXP '^(' || STRATEGY.APPLIED_SET_IDS || ')$'

    ORDER BY APPLIED_ORDER
) DESCRIPTIONS

GROUP BY 1,2

这给了我正在寻找的确切输出。