SQLITE - 正确地将行转换为列

时间:2012-08-03 16:58:39

标签: sql database sqlite

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

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在单个prod_color中使用MAX?

我希望你能理解我写的内容,提前谢谢,感谢任何帮助。

(如果您需要更清楚地解释一些问题,请尽快回答。

再次感谢,最好的问候

2 个答案:

答案 0 :(得分:7)

我添加了额外的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

答案 1 :(得分:0)

动态查询方法(每个数据透视列使用 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. 使用 pivot 表创建 rowmap 表。请注意,第三个枢轴“矩阵填充” 查询也只需要单列。

    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 temp.result_pivot;