【问题标题】:Very complex query with subqueries does not work带有子查询的非常复杂的查询不起作用
【发布时间】:2013-07-22 15:30:41
【问题描述】:

我有一个表格,其中包含一些来自能量计的值。一台小型计算机通过 RS485 读取一些寄存器并将值保存到 mysql 数据库。那部分工作正常。现在我在一个 PHP 网站上工作,它在图表中显示记录的数据。我可以选择一个范围,然后我只会从所选范围中获取值。该网站不应获得超过 1000 个值。正因为如此,我使用this question 来获取一个只选择每个第 n 行的代码(n 将根据有多少个值来计算)。

我已将我的 sql commant 放入 this SQL-Fiddle。我知道我不能在那里工作,因为那里有一些参数(?)。我只是想向你展示我的环境。

现在我的问题是:我怎样才能让我当前的查询正常工作/有什么问题?它有一个 JOIN,因为这些值必须乘以另一个表中的因子。

DeviceID 和 Register 将由 PHP (mysqli) 填写。

【问题讨论】:

  • 抱歉,但“帮助我完成复杂的查询”的问题在这里算作“过于本地化”。只有一种方法可以让某些东西正常工作 - 调试它。
  • 顺便说一句,选择每第 n 行都是糟糕的数据库设计。数据库中没有顺序,根本没有第n行。
  • 我已经尝试移除部件。
  • 这是必要的,因为我无法选择我范围内的所有值,我只想获得 1000 行,这就是为什么我必须选择每个第 n 行。

标签: mysql mysqli sqlfiddle


【解决方案1】:

你的 SQL 很奇怪。

您似乎有很多不使用的用户变量(@STARTDATE 和@ENDDATE)。您似乎还使用@REGISTER 和@DEVICEID 用户变量来避免必须为它们传递参数两次。

您在几个子查询中进一步初始化它们,但您为每个子查询使用相同的别名。

编辑 - 继续我的评论。

以下内容似乎可以满足您的需求,但并未经过实际测试确定。它只使用了 2 个参数。

SELECT * 
FROM 
(
    SELECT
        realvalues.`Value` * register.Factor,        
        realvalues.`Timestamp`,
        @X := @X + 1 AS rank,
        Sub1.JumpSize
    FROM realvalues
    JOIN register
    ON register.DeviceID = realvalues.DeviceID
    JOIN
    (
        SELECT DeviceID, Register, (1000 / COUNT(*)) AS JumpSize
        FROM realvalues 
        WHERE realvalues.DeviceID = ?
        AND realvalues.Register = ?
        GROUP BY DeviceID, Register
    ) Sub1
    ON Sub1.DeviceID = realvalues.DeviceID AND Sub1.Register = realvalues.Register 
    CROSS JOIN (SELECT @X := 0) t                               
) a
WHERE rank MOD GREATEST(JumpSize, 1) = 0

编辑

你去寻找一个可能的解决方案。

SELECT * 
FROM 
(
    SELECT  Register,        
            `Timestamp`,
            Readout,
            MOD(NumRecs , rank)
    FROM 
    (
        SELECT
            realvalues.Register,        
            realvalues.`Timestamp`,
            realvalues.`Value` * register.Factor AS Readout,
            @X := @X + 1 AS rank,
            Sub1.NumRecs
        FROM realvalues
        JOIN register
        ON register.DeviceID = realvalues.DeviceID
        AND register.Register = realvalues.Register
        JOIN
        (
            SELECT DeviceID, Register, COUNT(*) AS NumRecs
            FROM realvalues 
            WHERE realvalues.DeviceID = ?
            AND realvalues.Register = ?
            GROUP BY DeviceID, Register
        ) Sub1
        ON Sub1.DeviceID = realvalues.DeviceID AND Sub1.Register = realvalues.Register 
        CROSS JOIN (SELECT @X := 0) t   
        ORDER BY realvalues.`Timestamp`
    ) a
    ORDER BY MOD(NumRecs , rank) 
    LIMIT 1000
) b
ORDER BY `Timestamp`

这是获取按时间戳排序的寄存器/设备的所有详细信息以获取排名。然后它按记录总数除以排名的 mod 排序,并为您想要的记录数设置一个 LIMIT。这样做的想法是,它使总记录除以排名的记录最接近整数,希望是均匀分布的。然后按时间戳对结果进行排序。

但不确定它会那么高效!

有更多的戏,做事的方式非常不同:-

SELECT register.Register,
        DateRanges.RangeStart,
        IFNULL(AVG(realvalues.`Value` * register.Factor), 0)
FROM
(
    SELECT  1374473600 + (((1374483600 - 1374473600) / 1000) * (units.i + tens.i * 10 + hundreds.i * 100)) AS RangeStart,
            1374473600 + (((1374483600 - 1374473600) / 1000) * ( 1 + units.i + tens.i * 10 + hundreds.i * 100)) - 1 AS RangeEnd
    FROM (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
    CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
    CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) hundreds
) DateRanges
LEFT OUTER JOIN realvalues
ON UNIX_TIMESTAMP(realvalues.`Timestamp`) BETWEEN DateRanges.RangeStart AND DateRanges.RangeEnd AND realvalues.DeviceID = 1 AND realvalues.Register = 40001
LEFT OUTER JOIN register
ON register.DeviceID = realvalues.DeviceID AND register.Register = realvalues.Register
GROUP BY Register, DateRanges.RangeStart
ORDER BY Register, DateRanges.RangeStart

这是一个子查询,以获取您传递的最小和最大时间戳之间的 1000 个时间戳范围(使用 unix 时间戳)(此处使用 1374473600 和 1374483600 只是为了向您展示一个示例),然后针对实值进行左连接并注册以查找哪个读数进入哪个范围。然后只需使用 AVG 获取每个日期范围内的平均读数。

【讨论】:

  • 我应该放一些吗?并设置一些变量两次?
  • 这可能更容易,或者可能更容易只做一个将这些参数传递给它的子选择,然后基于它进行连接。将添加一个示例。
  • 是的,这就是您的基本原始 SQL 似乎正在尝试做的事情。您将 realvalues 与 deviceid 匹配的寄存器连接起来。使用您的测试数据,设备 ID 全部匹配,因此返回 9 x 10 = 90 行。但是您的 WHERE 子句仅检查 realvalues 表上的寄存器 ID。因此,对于设备 id 1 和寄存器 id 40001,它是来自实值的 3 行,然后是来自寄存器的 10 行 = 30 行返回。你能把你真正想要的东西(包括如何计算你想要的行)用英文写出来吗?我会照着做的。
  • 我将在 PHP 中选择所有值到一个数组中,并且只根据行数写入每个 nTH 行。大约有 4.000.000 行。那应该可以。
  • 对。有戏。问题是每第 n 行都会很奇怪。例如,假设您有 1001 条记录并希望返回 1000 条。您不能只获得每第 n 条记录。我有一个剧本,可能会想出一些东西。将为此修改我的帖子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 2018-11-24
  • 1970-01-01
  • 2013-08-18
  • 1970-01-01
  • 1970-01-01
  • 2020-03-31
相关资源
最近更新 更多