iT邦幫忙

2022 iThome 鐵人賽

DAY 21
0
Software Development

SQL rookie 之天天魯一下系列 第 21

Day 21 - Active Record Query Interface: group method(1)

  • 分享至 

  • xImage
  •  

Hi, 大家好~~

昨天練習了一下SQL injection 發生的情境和語法,今天則是要研究Query Interface 章節中也令人相當興奮的方法——group

我們先來想像一下使用的情境:


首先,利用之前產生的todos 資料表:todos 資料表

以及其欄位資訊:

  create_table "todos", force: :cascade do |t|
    t.integer "userId"
    t.text "title"
    t.boolean "completed"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

來模擬今天的情境——利用completed這欄來group,看看總共有幾筆為true、幾筆為false:

irb(main):009:0> Todo.group(:completed)
  Todo Load (3.1ms)  SELECT "todos".* FROM "todos" GROUP BY "todos"."completed"
Traceback (most recent call last):
ActiveRecord::StatementInvalid (PG::GroupingError: ERROR:  column "todos.id" must appear in the GROUP BY clause or be used in an aggregate function)
LINE 1: SELECT "todos".* FROM "todos" GROUP BY "todos"."completed"
               ^

不知道大家有沒有踩過這個坑?想說documents 上也是這麼用的呀:
API dock

但我們冷靜些,先來看看當我們這麼query 的時候,statement 和documents 上的有何不同?

SELECT "todos".* FROM "todos" GROUP BY "todos"."completed"

# vs.

SELECT "users".* FROM "users" GROUP BY name

# 似乎真的不同?

那我偷吃步跟Guide 這麼做:

sql = "SELECT \"todos\".* FROM \"todos\" GROUP BY \"completed\""

irb(main):030:0> Todo.find_by_sql(sql)
  Todo Load (1.9ms)  SELECT "todos".* FROM "todos" GROUP BY "completed"
Traceback (most recent call last):
        1: from (irb):30
ActiveRecord::StatementInvalid (PG::GroupingError: ERROR:  column "todos.id" must appear in the GROUP BY clause or be used in an aggregate function)
LINE 1: SELECT "todos".* FROM "todos" GROUP BY "completed"
               ^

等一下,這騙人的吧!why!!!雖然訊息有提示todos.id 也必須要在group 的query statement 內,但其實很不甘心的!為什麼Guide 裡User.group(:name)可以用呢,怒!

但我們先委屈、傲嬌地跟著修改一下吧:

Todo.group([:id, :completed])
Todo.group(:id, :completed)

# 可無論怎麼嘗試

irb(main):043:0> Todo.group([:id, :completed]).to_sql
"SELECT \"todos\".* FROM \"todos\" GROUP BY \"todos\".\"id\", \"todos\".\"completed\""
irb(main):044:0> Todo.group(:id, :completed).to_sql
"SELECT \"todos\".* FROM \"todos\" GROUP BY \"todos\".\"id\", \"todos\".\"completed\""

# SQL 的statement 還是一樣的

重點是回傳的資料一點也不像有被groupgroup_by

偷用圖形化工具查看呢?

Todo.group(:id, :completed)

怒欸!難道跟arguments 內的順序有關:

irb(main):047:0> Todo.group(:completed, :id).to_sql
"SELECT \"todos\".* FROM \"todos\" GROUP BY \"todos\".\"completed\", \"todos\".\"id\""

可還是得到了個令人無言又生氣的結果:
https://ithelp.ithome.com.tw/upload/images/20221005/20150959j2jGgpdWxU.png

當我們參考documents 時:
group 下半部
好像煞有其事的樣子!?

但無論怎麼修改arguments 的順序,都還是得到了個不具可讀性的結果:
https://ithelp.ithome.com.tw/upload/images/20221005/20150959Bl8yNEUDfc.png


跟著做到這邊,其實真的很想哭,一方面覺得自己無知,一方面覺得到底在做啥!!

且跟著API dock 去看他User 的欄位資訊有何不同?
https://ithelp.ithome.com.tw/upload/images/20221005/2015095947PPaHHrTf.png

阿就真的沒有啊:
https://ithelp.ithome.com.tw/upload/images/20221005/201509595Jko2BZmMs.png

所以這頁group解釋了什麼、或說明了什麼嗎?老實說我感覺不到啊啊啊啊!!!


雖然這句話倒是令人有點理解了什麼、可又不太能明白的感覺?

Returns an array with distinct records based on the group attribute:

翻譯蒟蒻:group會回傳一個根據attribute 且獨特的資料array

但還是不能解釋為什麼有group 和沒group... 都是根據id grouping 而不是用completed?

難怪總是回傳了200 筆資料!?


但如果我們利用這概念這麼做:

irb(main):076:0> Todo.select(:completed).group(:completed)
  Todo Load (1.3ms)  SELECT "todos"."completed" FROM "todos" GROUP BY "todos"."completed"
[
    [0] #<Todo:0x00007fc723f42eb8> {
               "id" => nil,
        "completed" => false
    },
    [1] #<Todo:0x00007fc723f42d28> {
               "id" => nil,
        "completed" => true
    }
]

好像就能得到近似我們要的結果了?

而再加上count

irb(main):078:0> Todo.select(:completed).group(:completed).count
   (0.7ms)  SELECT COUNT("todos"."completed") AS count_completed, "todos"."completed" AS todos_completed FROM "todos" GROUP BY "todos"."completed"
{
    false => 110,
     true => 90
}

所以說..... 究竟是為什麼呢?不要這樣搞我啊~~

但總之今天的研究就先到這裡囉,被翻爛的書先去沉澱一下自己的心情......


上一篇
Day 20 - Active Record Query Interface: SQL Injection
下一篇
Day 22 - Active Record Query Interface: group method(2)
系列文
SQL rookie 之天天魯一下30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言