簡體   English   中英

mysql子查詢中的未知列

[英]Unknown column in mysql subquery

我試圖得到一個項目的平均值,所以我使用子查詢。

更新 :我本來應該更清楚,但我希望avg僅用於最后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;

當我這樣做時,我在'where子句'中得到錯誤未知列'y.id' ,在進一步閱讀這里我相信這是因為當查詢進入下一級時,它們需要加入?

所以我嘗試了以下**刪除了不需要的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;

但我得到表'test.y'不存在 我在這里走在正確的軌道上嗎? 我需要改變什么來獲得我在這之后的目標?

這個例子可以在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

我不是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'有異常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小提琴演示

這是我的解決方案。 很容易理解它是如何工作的,但同時由於我使用的是一些字符串函數,因此它無法進行優化,而且它遠非標准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) |

然后我們可以將這個查詢的結果與products表本身連接起來,在連接條件下我們可以使用FIND_IN_SET(src,csvalues)返回逗號分隔值中src字符串的位置:

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

請看這里的小提琴。 我不建議對大表使用這個技巧,但對於小數據集,它很好。

這可能不是最簡單的解決方案,但它確實起到了作用,並且是一個有趣的變體,在我看來是透明的。 我模擬了我從Oracle中學到的分析函數。

因為我們不假設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小提琴演示: http ://sqlfiddle.com/#!2 / c13bc/129

我想感謝這個關於如何在mysql中模擬分析函數的答案: MySQL在ORDER BY中獲取行位置

看起來你只是想要:

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

內部查詢獲取添加到產品的最后5行,包裝的查詢獲得其存款的平均值。

我將稍微簡化您的查詢,以便我可以解釋它。

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;

這是在MySQL中實現它的一種方法:

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中運行良好。

這里有2個可能的解

首先使用用戶變量添加序列號。 這樣做兩次,並將第二個集合連接到序列號在id - 1和id - 5之間的第一個集合。然后只需使用AVG。 沒有相關的子查詢。

SELECT Sub3.id, Sub3.itemid, Sub3.deposit, AVG(Sub4.deposit)
FROM
(
    SELECT Sub1.id, Sub1.itemid, Sub1.deposit, @Seq:=@Seq+1 AS Sequence
    FROM
    (
        SELECT id, itemid, deposit
        FROM products
        ORDER BY id DESC
    ) Sub1
    CROSS JOIN
    (
        SELECT @Seq:=0
    ) Sub2
) Sub3
LEFT OUTER JOIN
(
    SELECT Sub1.id, Sub1.itemid, Sub1.deposit, @Seq1:=@Seq1+1 AS Sequence
    FROM
    (
        SELECT id, itemid, deposit
        FROM products
        ORDER BY id DESC
    ) Sub1
    CROSS JOIN
    (
        SELECT @Seq1:=0
    ) Sub2
) Sub4
ON Sub4.Sequence BETWEEN Sub3.Sequence + 1 AND Sub3.Sequence + 5
GROUP BY Sub3.id, Sub3.itemid, Sub3.deposit
ORDER BY Sub3.id DESC

第二個是較粗糙的,並使用相關的子查詢(隨着數據量的增加,這可能表現不佳)。 是否正常選擇但是對於最后一列,它具有引用主選擇中的id的子查詢。

SELECT id, itemid, deposit, (SELECT AVG(P2.deposit) FROM products P2 WHERE P2.id BETWEEN P1.id - 5 AND p1.id - 1 ORDER BY id DESC LIMIT 5)
FROM products P1
ORDER BY id DESC

這就是你追求的嗎?

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(因此你的WHERE子句); 其次,id不能在itemid內順序/沒有間隙。 第三,您可能希望生成跨越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
;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM