-5

エネルギー メーターから取得したいくつかの値を含むテーブルがあります。小さなコンピューターが RS485 経由でいくつかのレジスターを読み取り、その値を mysql データベースに保存します。その部分はうまく機能します。現在、記録されたデータをグラフに表示する PHP Web サイトに取り組んでいます。範囲を選択すると、選択した範囲からのみ値が取得されます。Web サイトが 1000 を超える値を取得することはありません。このため、この質問を使用して、n 番目の各行のみを選択するコードを取得しました (n は値の数に基づいて計算されます)。

SQL コマンドをthis SQL-Fiddleに配置しました。そこにはいくつかのパラメーター (?) があるため、そこで作業できないことはわかっています。私の環境を見せたかっただけです。

今私の質問:どうすれば現在のクエリを機能させることができますか/何が問題なのですか? 他のテーブルにある係数で値を乗算する必要があるため、JOIN があります。

DeviceID と Register は PHP (mysqli) によって入力されます。

4

1 に答える 1

1

あなたのSQLはかなり奇妙です。

使用していないユーザー変数 (@STARTDATE および @ENDDATE) が多数あるようです。また、 @REGISTER および @DEVICEID ユーザー変数を使用して、それらのパラメーターを 2 回渡す必要がないように思われます。

さらに、いくつかのサブクエリでこれらを初期化しますが、それらのサブクエリのそれぞれに同じエイリアス名を使用します。

編集 - 私のコメントにさらに。

以下はあなたが望むことをしているように見えますが、実際には確実にテストされていません。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`

これは、ランクを取得するためにタイムスタンプで並べ替えられたレジスタ/デバイスのすべての詳細を取得しています。次に、必要なレコード数の LIMIT を使用して、レコードの総数をランクで割った mod によって並べ替えられます。これのアイデアは、合計レコードをランク​​で割った値が整数に最も近くなり、うまくいけば均等に分散されるということです。次に、その結​​果がタイムスタンプ順に並べ替えられます。

ただし、それほど効率的かどうかはわかりません。

物事を行う非常に異なる方法で、より多くの遊びがありました:-

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 に答える