【问题标题】:SQLITE - transposing rows into columns properlySQLITE - 将行正确转换为列
【发布时间】:2012-08-03 16:58:39
【问题描述】:

我有一个包含如下产品订单表的数据库:

order_id | prod_code | prod_color | size | quantity |  
-----------------------------------------------------
1          SHIRT       001          S      10
1          SHIRT       001          M      7
1          SHIRT       001          L      8
1          SHIRT       001          XL     1
1          SHIRT       995          S      2
1          SHIRT       995          M      1
1          SHIRT       995          L      0
1          SHIRT       995          XL     1
2          PANTS       ....

还有这样的产品价格表:

prod_code | prod_color | price | currency
-----------------------------------------
SHIRT       001          10      EUR
SHIRT       001          9       USD
SHIRT       001          50      YEN
SHIRT       001          15      RUB
SHIRT       995          20      EUR
SHIRT       995          29      USD
SHIRT       995          100     YEN
SHIRT       995          45      RUB 
PANTS       ....  

我想要得到的是这样的视图(为简单起见,按 order_id 过滤):

order_id | prod_code | prod_color | size | quantity | EUR | USD | YEN | RUB
---------------------------------------------------------------------------
1          SHIRT       001          S      10         10    9     50    15
1          SHIRT       001          M      7          10    9     50    15
1          SHIRT       001          L      8          10    9     50    15
1          SHIRT       001          XL     1          10    9     50    15
1          SHIRT       995          S      2          20    29    100   45
1          SHIRT       995          M      1          20    29    100   45
1          SHIRT       995          L      0          20    29    100   45
1          SHIRT       995          XL     1          20    29    100   45

我已经浏览过网络和 SO,我发现 THIS QUESTION 准确地告诉了我我需要做什么,但我认为有一些修改......

我尝试在数据库上运行此查询:

SELECT o.order_id, o.prod_code, o.prod_color, o.size, o.quantity,
MAX(CASE WHEN p.currency = 'EUR' THEN p.price END) AS 'EUR',
MAX(CASE WHEN p.currency = 'USD' THEN p.price END) AS 'USD',
MAX(CASE WHEN p.currency = 'YEN' THEN p.price END) AS 'YEN',
MAX(CASE WHEN p.currency = 'RUB' THEN p.price END) AS 'RUB'
FROM products_order o JOIN products_prices p ON o.prod_code = p.prod_code
WHERE o.order_id = 1
GROUP BY o.order_id, o.prod_code, o.prod_color, o.size, o.quantity

将在价格列中返回,对于每一行,在整个列中找到的 MAX 值:所以对于 [product_color = 001] 和 [product_color = 995],我的价格都等于 20 欧元(对于 001,它是更便宜,只要 10 欧元!)

我也尝试过不使用 MAX,但它为每个价格提供了一行,让很多单元格为空(然后我明白了 MAX 的用途:D)。

你知道一种方法来做我想做的事吗?如何在单个 prod_color 中使用 MAX 而不是通过每个 prod_color 使用?

我希望你能理解我写的东西,在此先感谢,感谢任何帮助。

(如果您需要更清楚的解释,请询问并尽快回复。

再次感谢,问候

【问题讨论】:

标签: sql database sqlite


【解决方案1】:

我添加了一个额外的JOIN 条件:AND o.prod_color = p.prod_color

http://sqlfiddle.com/#!5/fadad/5

SELECT
  o.order_id, o.prod_code, o.prod_color, o.size, o.quantity,
  MAX(CASE WHEN p.currency = 'EUR' THEN p.price END) AS 'EUR',
  MAX(CASE WHEN p.currency = 'USD' THEN p.price END) AS 'USD',
  MAX(CASE WHEN p.currency = 'YEN' THEN p.price END) AS 'YEN',
  MAX(CASE WHEN p.currency = 'RUB' THEN p.price END) AS 'RUB'

FROM products_order o
JOIN products_prices p
  ON o.prod_code = p.prod_code
 AND o.prod_color = p.prod_color /* this line is added */

WHERE o.order_id = '1'
GROUP BY
  o.order_id,
  o.prod_code,
  o.prod_color,
  o.size,
  o.quantity

没有GROUP BY 的简化示例查询显示了差异:

http://sqlfiddle.com/#!5/fadad/13

【讨论】:

    【解决方案2】:

    动态查询方法(每个数据透视列使用CASE 表达式)应该是首选方法,但是当您预先不知道数据透视列或者由于某种原因难以在应用程序端生成查询时,有一个以pivot_vtab SQLite 虚拟表扩展的形式替代。在撰写本文时它不太实用,因为它似乎是一项概念验证工作,但这并不意味着它可以/不会改进。

    我不会详细介绍如何构建扩展程序——您可以在 related answer 中看到它们。 我正在使用这样的问题中的数据。

    CREATE TABLE products_order(
      order_id INTEGER,
      prod_code TEXT,
      prod_color INTEGER,
      size TEXT,
      quantity INTEGER
    );
    
    INSERT INTO products_order(order_id, prod_code, prod_color, size, quantity)
    VALUES
      (1, 'SHIRT', 001, 'S', 10),  (1, 'SHIRT', 001, 'M', 7),
      (1, 'SHIRT', 001, 'L', 8),   (1, 'SHIRT', 001, 'XL', 1),
      (1, 'SHIRT', 995, 'S', 2),   (1, 'SHIRT', 995, 'M', 1),
      (1, 'SHIRT', 995, 'L', 0),   (1, 'SHIRT', 995, 'XL', 1);
    
    CREATE TABLE products_prices(
      prod_code TEXT,
      prod_color INTEGER,
      price INTEGER,
      currency TEXT
    );
    
    INSERT INTO products_prices(prod_code, prod_color, price, currency)
    VALUES
      ('SHIRT', 001, 10, 'EUR'),  ('SHIRT', 001, 9, 'USD'),
      ('SHIRT', 001, 50, 'YEN'),  ('SHIRT', 001, 15, 'RUB'),
      ('SHIRT', 995, 20, 'EUR'),  ('SHIRT', 995, 29, 'USD'),
      ('SHIRT', 995, 100, 'YEN'), ('SHIRT', 995, 45, 'RUB');
    

    枢轴是这样构建的(我假设扩展程序已加载,.load ./pivot_vtab)。

    1. 因为扩展名仅限于一个“行”列(您在其上分组), 创建临时映射。我假设顺序键是(order_id, prod_code, prod_color, size)quantity 在功能上取决于order_id,所以没有 需要对其进行分组,但它将在第 3 步中使用。

      CREATE TABLE rowmap AS 
      SELECT 
        ROW_NUMBER() OVER(ORDER BY 1) row_id, 
        order_id,  
        prod_code,  
        prod_color,  
        size, 
        quantity
      FROM products_order
      GROUP BY 2, 3, 4, 5;
      
    2. 使用rowmap 表创建pivot 表。请注意,第三个枢轴“矩阵填充” 查询也只需要单列。

      CREATE VIRTUAL TABLE pivot USING pivot_vtab(
        (SELECT row_id FROM rowmap),
        (SELECT currency, currency FROM products_prices GROUP BY currency),   
        (
          SELECT price
          FROM products_order o
          JOIN products_prices p USING(prod_code, prod_color)
          JOIN rowmap m USING(order_id, prod_code, prod_color, size)
          WHERE o.order_id = 1 AND m.row_id = ?1 AND p.currency = ?2
          GROUP BY o.order_id, o.prod_code, o.prod_color, o.size, p.currency
        )
      );
      
    3. 重新组合rowmap 列。

      SELECT order_id, prod_code, prod_color, size, quantity, p.*
      FROM pivot p
      JOIN rowmap r ON p.row_id = r.row_id;
      

      结果集应该是这样的(我有.headers on.mode column)。

      order_id    prod_code   prod_color  size        quantity    row_id      EUR         RUB         USD         YEN       
      ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------
      1           SHIRT       1           L           8           1           10          15          9           50        
      1           SHIRT       1           M           7           2           10          15          9           50        
      1           SHIRT       1           S           10          3           10          15          9           50        
      1           SHIRT       1           XL          1           4           10          15          9           50        
      1           SHIRT       995         L           0           5           20          45          29          100       
      1           SHIRT       995         M           1           6           20          45          29          100       
      1           SHIRT       995         S           2           7           20          45          29          100       
      1           SHIRT       995         XL          1           8           20          45          29          100  
      
    4. 或者,如果您想删除 row_id 列,您可以使用另一个临时删除它 表(自 SQLite 3.35 起支持ALTER TABLE ... DROP COLUMN)。

      CREATE TABLE result_pivot AS 
      SELECT order_id, prod_code, prod_color, size, quantity, p.*
      FROM pivot p
      JOIN rowmap r ON p.row_id = r.row_id;
      
      ALTER TABLE result_pivot DROP COLUMN row_id;
      
      SELECT * FROM result_pivot;
      

    【讨论】:

    • pivot_vtab的作者已经实现了this enchantment,所以不再需要临时表了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-19
    • 1970-01-01
    • 2012-08-12
    • 1970-01-01
    • 2012-02-07
    相关资源
    最近更新 更多