SQLAlchemy查询基于时间戳从销售中返回分组产品

时间:2016-06-16 13:43:18

标签: python sql postgresql sqlalchemy

所以我有点坚持要求返回以下内容所需的逻辑:

+--------------+------------+------------+------------+
| Product Name | 2016-05-10 | 2016-05-11 | 2016-05-12 |
+--------------+------------+------------+------------+
| Big Chair    |   29.00    |   78.00    |   900.00   |
+--------------+------------+------------+------------+
| Small Chair  |   11.00    |   65.00    |   12.00    |
+--------------+------------+------------+------------+
| Box of bees  |    5.00    |   20.00    |   80.00    |
+--------------+------------+------------+------------+ ...

这就是为什么 - 我在SQLAlchemy models.py文件中的当前设置是(如果您发现它不是最佳的话,请不要向我扔砖头):

class Sale(db.Model):
    __tablename__ = 'sales'
    id = Column(Integer, primary_key=True)
    sale_date = Column(DateTime)
    total_price = Column(Numeric(12, 2), default=0)
    products = relationship('Product', secondary='sale_product')

class Product(db.Model):
    __tablename__ = 'products'
    id = Column(Integer, primary_key=True)
    name = Column(Text)
    price = Column(Numeric(12, 2), default=0)
    sales = relationship('Sale', secondary='sale_product')


class SaleProduct(db.Model):
    __tablename__ = 'sale_product'
    sale_id = Column(Integer, ForeignKey('sales.id'), primary_key=True)
    product_id = Column(Integer, ForeignKey('products.id'), primary_key=True)
    product_quantity = Column(Numeric(12, 1), default=0)
    sale = relationship('Sale', backref=backref("sale_assoc"))
    product = relationship('Product', backref=backref("product_assoc"))

然而,我正在努力想出一个可以返回上述结果的查询,或者更具体地说,我可以用来实现所述结果的对象。 我可以" hack"这使用python进行处理,但我认为让Postgres做繁重的工作更有效率。

到目前为止我的想法是

我需要首先以某种方式加入Product和Sale表,同时使用SaleProduct作为参考点(??),然后让SQLAlchemy按名称对产品进行分组(在这个例子中,假设名称总是唯一的所以没有两个产品会有相同的名称EVER)。 但我遇到的问题是当我试图弄清楚如何加入已经分组的产品以返回每个分组的成本之和,然后将其分成带日期的列。

更清晰:

  1. 我是否可以使用Postgres或SQL Alchemy实现顶级示例输出?
  2. 整个交易真的像我想的那样复杂吗?我在这里错过了什么?
  3. 最后 - 最好的方法是什么才能最终找到一个返回这样结果的方法?从数据库中获取原始数据是否更好,然后使用python进行操作(比如,将返回值转换为dicts或列表然后输出" rows"进行打印)或者我应该使用数据库本身这样的任务?
  4. 在这里非常厚颜无耻,但如果有人有空闲时间,他们是否可以提供某种模拟(在很小的范围内)甚至是如何工作的? - 不是必需的,但是如果可能的话,我很乐意接受实施。
  5. 提前谢谢大家。

1 个答案:

答案 0 :(得分:1)

你缺少的是crosstab(a.k.a。"数据透视表")。它允许您将列中的值转换为多列。

让我们倒退并提出我们需要提供给crosstab的数据,以便向我们提供我们想要的结果:

+--------------+------------+----------------+
| Product Name | Sale Date  | Total Quantity |
+--------------+------------+----------------+
| Big Chair    | 2016-05-10 | 29.00          |
+--------------+------------+----------------+
| Big Chair    | 2016-05-11 | 78.00          |
+--------------+------------+----------------+
| Big Chair    | 2016-05-12 | 900.00         |
+--------------+------------+----------------+
| Small Chair  | 2016-05-10 | 11.00          |
+--------------+------------+----------------+
| Small Chair  | 2016-05-11 | 65.00          |
+--------------+------------+----------------+
| Small Chair  | 2016-05-12 | 12.00          |
+--------------+------------+----------------+
| Box of Bees  | 2016-05-10 | 5.00           |
+--------------+------------+----------------+
| Box of Bees  | 2016-05-11 | 20.00          |
+--------------+------------+----------------+
| Box of Bees  | 2016-05-12 | 80.00          |
+--------------+------------+----------------+

我们写了什么查询来给我们这个?轻松,加入三个表格GROUP BY产品和销售日期:

SELECT
  products.name,
  sale_date::date,
  sum(product_quantity)
FROM
  sales
  JOIN sale_product ON sales.id = sale_id
  JOIN products ON product_id = products.id
GROUP BY products.name, sale_date::date

现在我们需要在日期上转动表格。

SELECT * FROM crosstab($$ <the previous query> $$, $$ SELECT DISTINCT sale_date::date FROM sales ORDER BY sale_date::date DESC LIMIT 3 $$)
AS sales_numbers(product_name text, today int, yesterday int, ereyesterday int);

对此的限制是必须预定义列,尤其是列数。要获取动态列,您可以EXECUTE动态构造查询。当然,为了获得最大的灵活性,您可以在Python中进行转换,但在SQL,IMO中执行此操作会稍微高效。

将此与SQLAlchemy一起使用留给读者练习。