在不使用扩展的情况下动态转置 SQLite 中的行

Posted

技术标签:

【中文标题】在不使用扩展的情况下动态转置 SQLite 中的行【英文标题】:Dynamically transpose rows in SQLite without using extensions 【发布时间】:2021-12-29 15:28:09 【问题描述】:

我在 SQLite 中创建查询时遇到问题。

我有两个通过 ID 关联的 SQL 表,我想在加入它们后转置其中的一些行。

由于我无法共享来自真实数据库的信息,因此我创建了一个玩具数据库来更好地说明我想要实现的目标。在这里,我有一个 MAINTABLE,包含它自己的 id 和更多东西,还有一个 SECONDARYTABLE,包含它自己的 id、对 MAINTABLE 的引用和键/值对。

MAINTABLE
idMainTable  MoreStuff
1            asdf
2            fdsa
3            hjkl
4            lkhj

SECONDARY TABLE
idSecondaryTable  idMainTable  key    value
1                 1            Key1       a
2                 1            Key5       s
3                 1            Key7       d
4                 1            Key8       f
5                 2            Key1       g
6                 2            Key4       h
7                 2            Key25      j
8                 3            Key2       l
9                 3            Key6       z
10                4            Key7       y

我想在这里做的是一个能够连接这两个表的查询,并将键和值行转换为这样的列,因此键是结果表中的列:

EXPECTED TABLE
idMainTable  MoreStuff   Key1  Key2  Key4  Key5  Key6  Key7  Key8 Key25
1                 asdf      a  null  null     s  null     d     f  null
2                 fdsa      g  null     h  null  null  null  null     j
3                 hjkl   null     l  null  null     z  null  null  null
4                 lkhj   null  null  null  null  null     y  null  null

我不介意键是否已排序,或者空单元格是否显示为 null 或空单元格。

我从this link 知道,当不同键的名称已知时,可以在此处应用条件聚合。但是,我不知道键的数量或键的可能名称,这就是我正在寻找动态解决方案的原因。在此链接中,还提出了一个名为pivot_vtab 的 SQLite 扩展,但扩展的使用在我的项目中是一个限制,我不能使用它。

mysql 中,可以选择使用 GROUP_CONCAT。我在 MySQL 中尝试过,它可以工作。但是,我一直在 SQLite 中尝试类似的方法,但我无法做到这一点。

这是在 MySQL 中运行的查询,给出了想要的结果:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(IF(keyColumn = ''',
      keyColumn,
      ''', value, NULL)) AS ',
      keyColumn
    )
  ) INTO @sql
FROM (MainTable INNER JOIN SecondaryTable ON MainTable.idMainTable =             
SecondaryTable.idMainTable);

SET @sql = CONCAT("SELECT SecondaryTable.idMainTable, ", @sql, 
            " FROM (MainTable INNER JOIN SecondaryTable ON MainTable.idMainTable = 
SecondaryTable.idMainTable)
            GROUP BY SecondaryTable.idMainTable");

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

这是在 SQLite 中创建玩具数据库的代码:

PRAGMA foreign_keys = ON;
BEGIN TRANSACTION;

CREATE TABLE IF NOT EXISTS `MainTable` (
  `idMainTable` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  `MoreStuff` VARCHAR(45) NOT NULL,
  UNIQUE (`idMainTable` ASC));

CREATE TABLE IF NOT EXISTS `SecondaryTable` (
  `idSecondaryTable` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  `idMainTable` INTEGER NOT NULL,
  `keyColumn` VARCHAR(45) NOT NULL,
  `value` VARCHAR(45) NOT NULL,
  UNIQUE (`idSecondaryTable` ASC),
  CONSTRAINT `fk_SecondaryTable_1`
    FOREIGN KEY (`idMainTable`)
    REFERENCES `MainTable` (`idMainTable`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION);

COMMIT;

BEGIN TRANSACTION;
INSERT INTO `MainTable` (`MoreStuff`) VALUES ('asdf');
INSERT INTO `MainTable` (`MoreStuff`) VALUES ('fdsa');
INSERT INTO `MainTable` (`MoreStuff`) VALUES ('hjkl');
INSERT INTO `MainTable` (`MoreStuff`) VALUES ('lkhj');

INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (1, 'Key1', 'a');
INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (1, 'Key5', 's');
INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (1, 'Key7', 'd');
INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (1, 'Key8', 'f');
INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (2, 'Key1', 'g');
INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (2, 'Key4', 'h');
INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (2, 'Key25', 'j');
INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (3, 'Key2', 'l');
INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (3, 'Key6', 'z');
INSERT INTO `SecondaryTable` (`idMainTable`, `keyColumn`, `value`) VALUES (4, 'Key7', 'y');

COMMIT;

出于测试目的,我使用以下online SQLite IDE 来生成所需的查询。

有没有一种方法可以使用 SQLite 实现我所描述的,无需扩展?

【问题讨论】:

SQLite 不支持动态 sql。 如果您事先知道要转置的列,您可以轻松实现它,但如果您想要动态地实现它,不幸的是不能仅使用 SQLite。您将需要使用您使用的任何编程语言加上 sqlite 采取两步。 我明白了。然后我将使用我正在使用的编程语言来生成新表。谢谢你们俩! 【参考方案1】:

看起来没有办法使用纯 SQLite 来实现这一点,就像 cmets 中显示的那样。

正如 cmets 中所建议的,可以使用另一种编程语言(例如 C++)构建动态查询。

以下链接也证实了这一点:

StackExchange: Is it possible to create Dynamic SQL statements purely in SQLite?

StackExchange: How to pivot data in SQLite

***: SQLITE transpose large number of rows into columns

如果以后这种情况发生变化,我会接受其他答案,或者如果我自己发现更新这个。

【讨论】:

以上是关于在不使用扩展的情况下动态转置 SQLite 中的行的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用count(*)的情况下在greenplum中查找表中的行数

是否可以在不运行 DbVisualizer + SQLite 中的原始 sql 的情况下更改列

在不计算的情况下获取 Spark 数据框中的行数

在给定条件下转置/重塑列中的行

SQLite3代码动态插入要删除的行数据的列值

在不安装sqlite3的时候使用sqlite3数据库以及问题/usr/bin/ld: skipping incompatible.....的解决