10

サブクエリを使用しているため、アイテムの平均を取得しようとしています。

更新:最初はもっと明確にするべきでしたが、平均は最後の5つのアイテムのみにしたいです

最初に私が始めた

SELECT 
y.id
FROM (
    SELECT *
        FROM (
                SELECT *
                FROM products
                WHERE itemid=1
        ) x  
    ORDER BY id DESC
    LIMIT 15 
) y;

これは実行されますが、ID が表示されるだけなので、かなり役に立ちません。

次に、以下に追加しました

SELECT
y.id,
(SELECT AVG(deposit) FROM (SELECT deposit FROM products WHERE id < y.id ORDER BY id DESC LIMIT 5)z) AVGDEPOSIT
FROM (
    SELECT *
        FROM (
                SELECT *
                FROM products
                WHERE itemid=1
        ) x  
    ORDER BY id DESC
    LIMIT 15 
) y;

これを行うと、エラーUnknown column 'y.id' in 'where clause'が表示されます。ここをさらに読むと、クエリが次のレベルに下がったときに結合する必要があるためだと思いますか?

だから私は以下を試しました**不要なsuqueryを削除しました

SELECT
y.id,
(SELECT AVG(deposit) FROM (
    SELECT deposit 
    FROM products
    INNER JOIN y as yy ON products.id = yy.id       
    WHERE id < yy.id 
    ORDER BY id DESC 
    LIMIT 5)z
    ) AVGDEPOSIT
FROM (
    SELECT *
    FROM products
    WHERE itemid=1
    ORDER BY id DESC
    LIMIT 15 
) y;

しかし、Table 'test.y' does not exist が表示されます。私はここで正しい軌道に乗っていますか?ここで求めているものを得るには、何を変更する必要がありますか?

この例は、こちらの sqlfiddle にあります。

CREATE TABLE products
    (`id` int, `itemid` int, `deposit` int);

    INSERT INTO products
    (`id`, `itemid`, `deposit`)
VALUES
(1, 1, 50),
(2, 1, 75),
(3, 1, 90),
(4, 1, 80),
(5, 1, 100),
(6, 1, 75),
(7, 1, 75),
(8, 1, 90),
(9, 1, 90),
(10, 1, 100);

この例のデータを考えると、予想される結果は以下のとおりです。各 ID の横に、前の 5 回の入金の平均を示す列があります。

id | AVGDEPOSIT
10 | 86 (deposit value of (id9+id8+id7+id6+id5)/5) to get the AVG
 9 | 84
 8 | 84
 7 | 84
 6 | 79
 5 | 73.75
4

8 に答える 8

6

私は MySQL の専門家ではありません (MS SQL ではもっと簡単に行うことができます)。あなたの質問は私には少し不明確に見えますが、前の 5 つの項目の平均を取得しようとしているようです。

ギャップのない Idがある場合は、簡単です。

select
    p.id,
    (
        select avg(t.deposit)
        from products as t
        where t.itemid = 1 and t.id >= p.id - 5 and t.id < p.id
    ) as avgdeposit
from products as p
where p.itemid = 1
order by p.id desc
limit 15

そうでない場合は、このクエリを次のように実行しようとしました

select
    p.id,
    (
        select avg(t.deposit)
        from (
            select tt.deposit
            from products as tt
            where tt.itemid = 1 and tt.id < p.id
            order by tt.id desc
            limit 5
        ) as t
    ) as avgdeposit
from products as p
where p.itemid = 1
order by p.id desc
limit 15

しかし、私には例外がありますUnknown column 'p.id' in 'where clause'。MySQL はサブクエリの 2 レベルのネストを処理できないようです。しかし、次のように を使用して 5 つの以前のアイテムを取得できますoffset

select
    p.id,
    (
        select avg(t.deposit)
        from products as t
        where t.itemid = 1 and t.id > coalesce(p.prev_id, -1) and t.id < p.id
    ) as avgdeposit
from 
(
    select
        p.id,
        (
            select tt.id
            from products as tt
            where tt.itemid = 1 and tt.id <= p.id
            order by tt.id desc
            limit 1 offset 6
        ) as prev_id
    from products as p
    where p.itemid = 1
    order by p.id desc
    limit 15
) as p

sql fiddle demo

于 2013-10-15T06:10:58.207 に答える
5

これが私の解決策です。それがどのように機能するかを理解するのは簡単ですが、同時に、いくつかの文字列関数を使用しているため、あまり最適化できません。また、標準 SQL からかけ離れています。いくつかのレコードを返すだけでよい場合は、それでも問題ない可能性があります。

このクエリは、すべての ID について、前の ID のカンマ区切りのリストを昇順に並べて返します。

SELECT p1.id, p1.itemid, GROUP_CONCAT(p2.id ORDER BY p2.id DESC) previous_ids
FROM
  products p1 LEFT JOIN products p2
  ON p1.itemid=p2.itemid AND p1.id>p2.id
GROUP BY
  p1.id, p1.itemid
ORDER BY
  p1.itemid ASC, p1.id DESC

次のようなものが返されます。

| ID | ITEMID |      PREVIOUS_IDS |
|----|--------|-------------------|
| 10 |      1 | 9,8,7,6,5,4,3,2,1 |
|  9 |      1 |   8,7,6,5,4,3,2,1 |
|  8 |      1 |     7,6,5,4,3,2,1 |
|  7 |      1 |       6,5,4,3,2,1 |
|  6 |      1 |         5,4,3,2,1 |
|  5 |      1 |           4,3,2,1 |
|  4 |      1 |             3,2,1 |
|  3 |      1 |               2,1 |
|  2 |      1 |                 1 |
|  1 |      1 |            (null) |

次に、このクエリの結果を製品テーブル自体と結合し、結合条件で、コンマ区切り値内の src 文字列の位置を返す FIND_IN_SET(src, csvalues) を使用できます。

ON FIND_IN_SET(id, previous_ids) BETWEEN 1 AND 5

最終的なクエリは次のようになります。

SELECT
  list_previous.id,
  AVG(products.deposit)
FROM (
  SELECT p1.id, p1.itemid, GROUP_CONCAT(p2.id ORDER BY p2.id DESC) previous_ids
  FROM
    products p1 INNER JOIN products p2
    ON p1.itemid=p2.itemid AND p1.id>p2.id
  GROUP BY
    p1.id, p1.itemid
  ) list_previous LEFT JOIN products
  ON list_previous.itemid=products.itemid
     AND FIND_IN_SET(products.id, previous_ids) BETWEEN 1 AND 5
GROUP BY
  list_previous.id
ORDER BY
  id DESC

ここでフィドルを参照してください。このトリックを大きなテーブルに使用することはお勧めしませんが、小さなデータ セットの場合は問題ありません。

于 2013-10-16T13:29:22.070 に答える
3

これはおそらく最も単純な解決策ではないかもしれませんが、それは仕事をし、興味深いバリエーションであり、私の意見では透過的です. オラクルから知っている分析関数をシミュレートします。

が連続しているとは想定していないためid、行のカウントは各行で @rn を増やすことによってシミュレートされます。rownum を含む次の製品テーブルはそれ自体と結合され、行 2 ~ 6 のみが平均の構築に使用されます。

select p2id, avg(deposit), group_concat(p1id order by p1id desc), group_concat(deposit order by p1id desc)
  from ( select p2.id p2id, p1.rn p1rn, p1.deposit, p2.rn p2rn, p1.id p1id
           from   (select p.*,@rn1:=@rn1+1 as rn from products p,(select @rn1 := 0) r) p1
                , (select p.*,@rn2:=@rn2+1 as rn from products p,(select @rn2 := 0) r) p2 ) r
  where p2rn-p1rn between 1 and 5
  group by p2id
  order by p2id desc
  ;

結果:

+------+--------------+---------------------------------------+------------------------------------------+
| p2id | avg(deposit) | group_concat(p1id order by p1id desc) | group_concat(deposit order by p1id desc) |
+------+--------------+---------------------------------------+------------------------------------------+
|   10 |      86.0000 | 9,8,7,6,5                             | 90,90,75,75,100                          |
|    9 |      84.0000 | 8,7,6,5,4                             | 90,75,75,100,80                          |
|    8 |      84.0000 | 7,6,5,4,3                             | 75,75,100,80,90                          |
|    7 |      84.0000 | 6,5,4,3,2                             | 75,100,80,90,75                          |
|    6 |      79.0000 | 5,4,3,2,1                             | 100,80,90,75,50                          |
|    5 |      73.7500 | 4,3,2,1                               | 80,90,75,50                              |
|    4 |      71.6667 | 3,2,1                                 | 90,75,50                                 |
|    3 |      62.5000 | 2,1                                   | 75,50                                    |
|    2 |      50.0000 | 1                                     | 50                                       |
+------+--------------+---------------------------------------+------------------------------------------+

SQL Fiddle デモ: http://sqlfiddle.com/#!2/c13bc/129

mysql で分析関数をシミュレートする方法に関するこの回答に感謝したい: MySQL get row position in ORDER BY

于 2013-10-18T14:14:40.990 に答える
2

あなたが望むように見えます:

SELECT
  id,
  (SELECT AVG(deposit)
   FROM (
        SELECT deposit
        FROM products
        ORDER BY id DESC
        LIMIT 5) last5
   ) avgdeposit
FROM products 

内側のクエリは、product に追加された最後の 5 行を取得します。このクエリは、預金の平均を取得します。

于 2013-10-15T06:17:21.537 に答える
0

MySQL でそれを行う 1 つの方法を次に示します。

SELECT p.id
     , ( SELECT AVG(deposit)
           FROM ( SELECT @rownum:=@rownum+1 rn, deposit, id
                    FROM ( SELECT @rownum:=0 ) r
                       , products
                   ORDER BY id ) t
          WHERE rn BETWEEN p.rn-5 AND p.rn-1 ) avgdeposit
  FROM ( SELECT @rownum1:=@rownum1+1 rn, id
           FROM ( SELECT @rownum1:=0 ) r
              , products
          ORDER BY id ) p
 WHERE p.rn >= 5
 ORDER BY p.rn DESC;

MySQL が WITH 句またはウィンドウ関数をサポートしていないのは残念です。両方を使用すると、クエリが次のように大幅に簡素化されます。

WITH tbl AS (
    SELECT id, deposit, ROW_NUMBER() OVER(ORDER BY id) rn
      FROM products
)
SELECT id
     , ( SELECT AVG(deposit)
           FROM tbl
          WHERE rn BETWEEN t.rn-5 AND t.rn-1 ) 
  FROM tbl t
 WHERE rn >= 5
 ORDER BY rn DESC;

後者のクエリは、Postgres で正常に実行されます。

于 2013-10-17T02:42:22.987 に答える
0

これはあなたが求めているものですか?

SELECT m.id
     , AVG(d.deposit) 
  FROM products m
     , products d
 WHERE d.id < m.id
   AND d.id >= m.id - 5
 GROUP BY m.id
 ORDER BY m.id DESC
;

しかし、そう簡単にはいきません。まず、テーブルに itemid を 1 つだけ含めることはできません (したがって、WHERE 句)。第 2 に、id を連続したものにすることはできず、itemid 内にギャップがないことです。第 3 に、一度に 1 つの itemid ではなく、itemid にまたがって実行されるものを生成したい場合があります。だからここにあります。

SELECT itemid
     , m_id as id
     , AVG(d.deposit) as deposit
  FROM (
        SELECT itemid
             , m_id
             , d_id
             , d.deposit
             , @seq := (CASE WHEN m_id = d_id THEN 0 ELSE @seq + 1 END) seq
          FROM (
                SELECT m.itemid
                     , m.id m_id
                     , d.id d_id
                     , d.deposit
                  FROM products m
                     , products d
                 WHERE m.itemid = d.itemid
                   AND d.id <= m.id
                 ORDER BY m.id DESC
                     , d.id DESC) d
             , (SELECT @seq := 0) s
        ) d
 WHERE seq BETWEEN 1 AND 5
 GROUP BY itemid
     , m_id
 ORDER BY itemid 
     , m_id DESC
;
于 2013-10-19T17:23:37.013 に答える
0

説明できるように、クエリを少し単純化します。

SELECT
y.id,
(
    SELECT AVG(deposit) FROM 
    (
        SELECT deposit 
        FROM products
        LIMIT 5
    ) z
) AVGDEPOSIT
FROM 
(
    SELECT *
    FROM 
    (
        SELECT *
        FROM products
    ) x  
    LIMIT 15 
) y;

ASそこにいくつかのキーワードを挿入するだけでよいと思います。他の誰かがもっとエレガントなものを考え出すと思いますが、今のところ試してみることができます.

SELECT
y.id,
(
    SELECT AVG(deposit) FROM 
    (
        SELECT deposit 
        FROM products
        LIMIT 5
    ) z
) AS AVGDEPOSIT
FROM 
(
    SELECT *
    FROM 
    (
        SELECT *
        FROM products
    ) AS x
    LIMIT 15 
) y;
于 2013-10-13T00:09:51.287 に答える