【问题标题】:Complex query in RailsRails 中的复杂查询
【发布时间】:2017-12-29 02:57:30
【问题描述】:

我正在尝试为VRP 构建一个简单的求解器Web 应用程序。我的问题是如何构建一个查询,返回每个客户的用户名、经度、纬度和他的产品以及所有产品列产品最后库存的数量?

数据库示意图: 客户和用户的关系是一对一的(用户是客户的超类)

Salesman 和 User 的关系是一对一的(User 是 Salesman 的超类)

销售员和客户关系是一对多的(销售员有很多客户)

客户和产品的关系是一对多的(一个客户有很多产品)

产品和库存关系是一对多的(需要记录每次库存变化)

欢迎任何改变数据库方案的建议

【问题讨论】:

  • 您能否发布您的模型之间的关系,特别是用户、客户、产品和库存。另外,客户与用户有何不同(您能从商业角度解释它们的不同之处吗)?
  • @the12 我们假设产品的最新库存是销售人员必须交付给客户的当前库存。另请参阅对关系的修改
  • 如果“最近的库存水平”是一个经常需要的值,那么我会考虑专门为它分配一个表,或者用它的值更新产品表。这可能是十秒查询和一秒查询之间的区别。当然取决于数据量等。

标签: sql ruby-on-rails activerecord database-design


【解决方案1】:

由于您的数据结构如此复杂,使用 ActiveRecord 对象检索所有内容将变得非常慢非常快(最近遇到了这个问题)。

让我们看看我们能一起解决什么问题。

由于您没有提供 MySQL 问题的示例数据,我自己填写了,但请随时改进数据,我们可能会找到更好的解决方案。

架构

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(6) unsigned NOT NULL,
  `username` varchar(200) NOT NULL,
  `longitude` varchar(200) NOT NULL,
   `latitude` varchar(200) NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `customers` (
  `id` int(6) unsigned NOT NULL,
    `user_id` int(6) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `products` (
  `id` int(6) unsigned NOT NULL,
    `customer_id` int(6) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `stocks` (
  `id` int(6) unsigned NOT NULL,
  `product_id` int(6) unsigned NOT NULL,
  `quantity` int(200) unsigned NOT NULL,
  `updated_at` DATETIME,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

INSERT INTO `users` (`id`, `username`, `longitude`, `latitude`) VALUES
  (1, 'username1', '11.12', '22.33'),
  (2, 'username2', '11.12', '22.33'),
  (3, 'username3', '11.12', '22.33');
INSERT INTO `customers` (`id`, `user_id`) VALUES
  (9, 1),
  (10, 2),
  (11, 3);
INSERT INTO `products` (`id`, `customer_id`) VALUES
  (33, 9),
  (34, 10),
  (35, 11);
INSERT INTO `stocks` (`id`, `product_id`, `quantity`, `updated_at`) VALUES
  (1, 33, 12, '2017-07-17 13:24:01'),
  (2, 34, 1, '2017-07-17 13:24:02'),
  (3, 35, 99, '2017-07-17 13:24:03'),
  (4, 36, 3, '2017-07-17 13:31:01');

查询

SELECT users.id, username, longitude, latitude, products.id as product_id, quantity from `users`
INNER JOIN customers ON user_id = users.id
INNER JOIN products ON customer_id = customers.id
INNER JOIN stocks ON product_id = products.id
ORDER BY stocks.updated_at DESC

结果

id  username    longitude   latitude    product_id  quantity
1   username1   11.12       22.33       33          12
2   username2   11.12       22.33       34          1
3   username3   11.12       22.33       35          99

对于 Rails 部分,您可以使用如下内容:

data = Users.select("id, longitude etc").
  joins(
    "INNER JOIN customers ON user_id = users.id etc "
)

SQL Fiddle link

【讨论】:

    【解决方案2】:

    您实际上不需要为每个模型构建查询。

    您可以从为客户构建一个查询开始:@customers = Customer.all,然后通过不同模型之间存在的关系从那里访问您想要的任何数据。

    即:

    查询客户的用户名、经度和纬度(以第一个客户为例,但可以使用Customer.where(:user_id => some_number).find查询。

    customer = Customer.all.first
    customer.user.username
    customer.user.longtitude
    customer.user.latitude
    

    下面的查询适用于获取第一个客户的第一个产品,以及该产品的第一个库存。再次,您可以使用不同的方法,例如 .find.where 来查询您需要的确切产品和结果库存。

    customer.products.first.stocks.first 
    

    如果您想获取所有客户(使用 @customers = Customer.all 的实例变量并循环遍历每个客户并输出结果,它看起来像这样:

    @customers.each do |customer|
     customer.username
    end
    

    【讨论】:

    • 是的,但是如果您想按客户产品数量的总和对客户进行排序,即使对于 30 位客户每人 80 件产品来说也不会那么简单/高效。这就是为什么我想要一个“面向 sql”的解决方案而不是“feth 一切并操纵对象数组”
    • IMO,它们是两个不同的东西。一个是你应该如何获取你需要的所有模型/属性。这可以使用我展示的 ActiveRecord 关联来完成。另一个是您应该如何使用复杂查询对这些结果进行专门排序,例如“按客户产品数量的总和对客户进行排序”。对于该查询,您应该在数据库中为模型客户提供一列产品数量。每次更改产品的数量时,它都应该更新该列。然后您可以使用Customer.all.order(:total_quantity => :asc) 进行查询。
    【解决方案3】:

    您可以在模型类中添加方法,并通过相关关系返回您需要的一切。浏览 Rails 文档活动记录

    【讨论】:

      猜你喜欢
      • 2013-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多