-5

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

我已经把我的 sql 命令放到了这个 SQL-Fiddle中。我知道我不能在那里工作,因为那里有一些参数(?)。我只是想向你展示我的环境。

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

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

4

1 回答 1

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 获取每个日期范围内的平均读数。

于 2013-07-23T14:10:25.287 回答