ActiveRecord ທຽບກັບ Ecto ສ່ວນທີສອງ

ນີ້ແມ່ນພາກສ່ວນທີສອງຂອງຊຸດ "ActiveRecord ທຽບກັບ Ecto", ໃນທີ່ Batman ແລະ Batgirl ຕໍ່ສູ້ກັບຖານຂໍ້ມູນການສອບຖາມແລະພວກເຮົາປຽບທຽບ ໝາກ ໂປມແລະ ໝາກ ກ້ຽງ.

ຫຼັງຈາກຊອກຫາໂຄງຮ່າງຖານຂໍ້ມູນແລະການເຄື່ອນຍ້າຍໃນ ActiveRecord vs ສ່ວນ ໜຶ່ງ ຂອງ Ecto, ໂພສນີ້ຈະກວມເອົາທັງ ActiveRecord ແລະ Ecto ຊ່ວຍໃຫ້ນັກພັດທະນາສາມາດສອບຖາມຖານຂໍ້ມູນໄດ້, ແລະທັງ ActiveRecord ແລະ Ecto ປຽບທຽບແນວໃດເມື່ອພົວພັນກັບຂໍ້ ກຳ ນົດດຽວກັນ. ພ້ອມກັນນີ້, ພວກເຮົາຍັງຈະໄດ້ຄົ້ນຫາຕົວຕົນຂອງ Batgirl ປີ 1989–2011.

ຂໍ້ມູນເມັດພັນ

ເລີ່ມຕົ້ນແລ້ວ! ອີງຕາມໂຄງສ້າງຖານຂໍ້ມູນທີ່ໄດ້ ກຳ ນົດໄວ້ໃນຂໍ້ຄວາມ ທຳ ອິດຂອງຊຸດນີ້, ສົມມຸດວ່າຜູ້ໃຊ້ແລະຕາຕະລາງໃບແຈ້ງ ໜີ້ ມີຂໍ້ມູນດັ່ງຕໍ່ໄປນີ້ທີ່ເກັບໄວ້ໃນນັ້ນ:

ຜູ້ໃຊ້

* ພາກສະ ໜາມ _ ສ້າງຕັ້ງຂອງ ActiveRecord ແມ່ນໃສ່ຊື່ໃສ່__ໃນ Ecto ໂດຍຄ່າເລີ່ມຕົ້ນ.

ໃບເກັບເງິນ

* ພາກສະ ໜາມ _ ສ້າງຕັ້ງຂອງ ActiveRecord ແມ່ນໃສ່ຊື່ໃສ່__ໃນ Ecto ໂດຍຄ່າເລີ່ມຕົ້ນ.

ການສອບຖາມທີ່ປະຕິບັດຜ່ານບົດຄວາມນີ້ສົມມຸດວ່າຂໍ້ມູນຂ້າງເທິງແມ່ນເກັບໄວ້ໃນຖານຂໍ້ມູນ, ສະນັ້ນຈົ່ງຈື່ ຈຳ ຂໍ້ມູນນີ້ໄວ້ໃນເວລາອ່ານ

ຊອກຫາລາຍການໂດຍໃຊ້ຄີຫລັກຂອງມັນ

ເລີ່ມຕົ້ນດ້ວຍການບັນທຶກຂໍ້ມູນຈາກຖານຂໍ້ມູນໂດຍໃຊ້ຫຼັກຂອງມັນ.

ActiveRecord

irb (ຕົ້ນຕໍ): 001: 0> User.find (1) User Load (0.4ms) SELECT "ຜູ້ໃຊ້". * ຈາກ "ຜູ້ໃຊ້" ບ່ອນທີ່ "ຜູ້ໃຊ້". "id" = $ 1 ຈຳ ກັດ $ 2 [["id", 1 ], ["ຈຳ ກັດ", 1]] => # <ຜູ້ໃຊ້ id: 1, full_name: "Bette Kane", ອີເມວ: "bette@kane.test", created_at: "2018-01-01 10:01:00" , update_at: "2018-01-01 10:01:00">

Ecto

iex (3)> Repo.get (ຜູ້ໃຊ້, 1)
[debug] QUERY OK source = "ຜູ້ໃຊ້" db = 5.2ms ຖອດລະຫັດ = 2.5ms ແຖວ = 0.1ms
SELECT u0. "id", u0. "full_name", u0. "ອີເມວ", u0. "ໃສ່ແລ້ວ", ".
% Financex.Accounts.User {
  __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ຜູ້ໃຊ້">,
  ອີເມວ: "bette@kane.test",
  full_name: "Bette Kane",
  id: 1,
  inserted_at: ~ N [2018-01-01 10: 01: 00.000000],
  ໃບເກັບເງິນ: # Ecto.Association.NotLoaded ,
  update_at: ~ N [2018-01-01 10: 01: 00.000000]
}

ປຽບທຽບ

ກໍລະນີທັງສອງແມ່ນຂ້ອນຂ້າງຄ້າຍຄືກັນ. ActiveRecord ອີງໃສ່ວິທີການຊອກຫາຂອງ class class model. ມັນ ໝາຍ ຄວາມວ່າທຸກໆຊັ້ນຮຽນຂອງເດັກນ້ອຍ ActiveRecord ມີວິທີການຊອກຫາຂອງມັນເອງຢູ່ໃນນັ້ນ.

Ecto ໃຊ້ວິທີການທີ່ແຕກຕ່າງກັນ, ໂດຍອີງໃສ່ແນວຄວາມຄິດຂອງ Repository ເປັນຜູ້ໄກ່ເກ່ຍລະຫວ່າງຊັ້ນແຜນທີ່ແລະໂດເມນ. ເມື່ອໃຊ້ Ecto, ໂມດູນຜູ້ໃຊ້ບໍ່ມີຄວາມຮູ້ກ່ຽວກັບວິທີການຊອກຫາຕົວມັນເອງ. ຄວາມຮັບຜິດຊອບດັ່ງກ່າວແມ່ນມີຢູ່ໃນໂມດູນຂອງ Repo, ເຊິ່ງສາມາດສ້າງແຜນທີ່ໃຫ້ກັບຖານຂໍ້ມູນພາຍໃຕ້ເຊິ່ງໃນກໍລະນີຂອງພວກເຮົາແມ່ນ Postgres.

ເມື່ອປຽບທຽບການສອບຖາມຂອງ SQL ເອງ, ພວກເຮົາສາມາດເຫັນຈຸດແຕກຕ່າງບາງຢ່າງ:

  • ActiveRecord ໂຫລດທຸກຂົງເຂດ (ຜູ້ໃຊ້. *), ໃນຂະນະທີ່ Ecto ໂຫລດພຽງແຕ່ທົ່ງນາທີ່ລະບຸໄວ້ໃນນິຍາມ schema.
  • ActiveRecord ປະກອບມີຂໍ້ ຈຳ ກັດ 1 ສຳ ລັບການສອບຖາມ, ໃນຂະນະທີ່ Ecto ບໍ່ມີ.

ໄປຫາທຸກລາຍການ

ໄປບາດກ້າວຕໍ່ໄປແລະໂຫລດຜູ້ໃຊ້ທຸກຄົນຈາກຖານຂໍ້ມູນ.

ActiveRecord

irb (ຕົ້ນຕໍ): 001: 0> User.all User Load (0.5ms) SELECT "ຜູ້ໃຊ້". * ຈາກ "ຜູ້ ນຳ ໃຊ້" ຈຳ ກັດ $ 1 [["ຈຳ ກັດ", 11]] => # , # , # <ຜູ້ໃຊ້ id: 3, full_name:" Cassandra Cain ", ອີເມວ:" cassandra@cain.test ", created_at:" 2018-01-03 10:03:00 ", update_at:" 2018-01-03 10:03:00 ">, # ]>

Ecto

iex (4)> Repo.all (ຜູ້ໃຊ້)
[debug] QUERY OK source = "ຜູ້ໃຊ້" db = 2.8ms ຖອດລະຫັດ = 0.2ms ແຖວ = 0.2ms
SELECT u0. "id", u0. "full_name", u0. "ອີເມວ", u0. "ໃສ່ແລ້ວ", ".
[
  % Financex.Accounts.User {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ຜູ້ໃຊ້">,
    ອີເມວ: "bette@kane.test",
    full_name: "Bette Kane",
    id: 1,
    inserted_at: ~ N [2018-01-01 10: 01: 00.000000],
    ໃບເກັບເງິນ: # Ecto.Association.NotLoaded ,
    update_at: ~ N [2018-01-01 10: 01: 00.000000]
  },,
  % Financex.Accounts.User {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ຜູ້ໃຊ້">,
    ອີເມວ: "barbara@gordon.test",
    full_name: "Barbara Gordon",
    id: 2,
    insert_at: ~ N [2018-01-02 10: 02: 00.000000],
    ໃບເກັບເງິນ: # Ecto.Association.NotLoaded ,
    update_at: ~ N [2018-01-02 10: 02: 00.000000]
  },,
  % Financex.Accounts.User {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ຜູ້ໃຊ້">,
    ອີເມວ: "cassandra@cain.test",
    full_name: "ກາຕາດາກາກາ",
    id: 3,
    insert_at: ~ N [2018-01-03 10: 03: 00.000000],
    ໃບເກັບເງິນ: # Ecto.Association.NotLoaded ,
    update_at: ~ N [2018-01-03 10: 03: 00.000000]
  },,
  % Financex.Accounts.User {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ຜູ້ໃຊ້">,
    ອີເມວ: "stephanie@brown.test",
    ຊື່ເຕັມ: "Stephanie Brown",
    id: 4,
    inserted_at: ~ N [2018-01-04 10: 04: 00.000000],
    ໃບເກັບເງິນ: # Ecto.Association.NotLoaded ,
    update_at: ~ N [2018-01-04 10: 04: 00.000000]
  }
]

ປຽບທຽບ

ມັນປະຕິບັດຕາມຮູບແບບດຽວກັນກັບພາກກ່ອນ. ActiveRecord ໃຊ້ທຸກວິທີການຮຽນແລະ Ecto ຂື້ນກັບຮູບແບບຂອງຫໍໄຕເພື່ອໂຫລດບັນທຶກ.

ມັນມີຄວາມແຕກຕ່າງບາງຢ່າງອີກໃນການສອບຖາມ SQL:

ສອບຖາມດ້ວຍເງື່ອນໄຂ

ມັນບໍ່ ໜ້າ ຈະເປັນໄປໄດ້ທີ່ພວກເຮົາຕ້ອງເກັບເອົາບັນທຶກທັງ ໝົດ ຈາກຕາຕະລາງ. ຄວາມຕ້ອງການທົ່ວໄປແມ່ນການ ນຳ ໃຊ້ເງື່ອນໄຂ, ເພື່ອກັ່ນຕອງຂໍ້ມູນທີ່ສົ່ງຄືນ.

ຂໍໃຫ້ໃຊ້ຕົວຢ່າງນັ້ນເພື່ອບອກໃບແຈ້ງ ໜີ້ ທັງ ໝົດ ທີ່ຍັງຕ້ອງຈ່າຍ (WHERE pay_at IS NULL).

ActiveRecord

irb (ຕົ້ນຕໍ): 024: 0> Invoice.where (pay_at: nil) Invoice Load (18.2ms) SELECT "ໃບແຈ້ງ ໜີ້". * ຈາກ "ໃບເກັບເງິນ" ບ່ອນໃດທີ່ "ໃບແຈ້ງ ໜີ້". , 11]] => # ]>

Ecto

iex (19)> ບ່ອນທີ່ (ໃບເກັບເງິນ, [i], is_nil (i.paid_at)) |> Repo.all ()
[debug] QUERY OK source = "ໃບເກັບເງິນ" db = 20.2ms
SELECT i0. "id", i0. "payment_method", i0. "pay_at", i0. "user_id", i0. "insert_at", i0. "updates_at" FROM "ໃບເກັບເງິນ" AS i0 WHERE (i0 "ຈ່າຍ" IS " NULL) []
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 3,
    inserted_at: ~ N [2018-01-04 08: 00: 00.000000],
    pay_at: nil,
    payment_method: nil,
    update_at: ~ N [2018-01-04 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 3
  },,
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 4,
    inserted_at: ~ N [2018-01-04 08: 00: 00.000000],
    pay_at: nil,
    payment_method: nil,
    update_at: ~ N [2018-01-04 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 4
  }
]

ປຽບທຽບ

ໃນທັງສອງຕົວຢ່າງ, ບ່ອນທີ່ ຄຳ ຫລັກຖືກ ນຳ ໃຊ້, ເຊິ່ງແມ່ນການເຊື່ອມຕໍ່ກັບຂໍ້ ກຳ ນົດຂອງ SQL WHERE. ເຖິງແມ່ນວ່າການສອບຖາມ SQL ທີ່ຜະລິດແມ່ນຂ້ອນຂ້າງຄ້າຍຄືກັນ, ວິທີການທີ່ເຄື່ອງມືທັງສອງມີຢູ່ມັນມີຄວາມແຕກຕ່າງທີ່ ສຳ ຄັນ.

ActiveRecord ຫັນປ່ຽນການຈ່າຍເງິນ: ການໂຕ້ຖຽງ nil ກັບໃບລາຍງານ IS_ NULL SQL ໂດຍອັດຕະໂນມັດ. ເພື່ອໃຫ້ໄດ້ຮັບຜົນຜະລິດດຽວກັນໂດຍໃຊ້ Ecto, ນັກພັດທະນາຕ້ອງມີຄວາມລະອຽດເພີ່ມເຕີມກ່ຽວກັບຄວາມຕັ້ງໃຈຂອງພວກເຂົາ, ໂດຍການໂທຫາ is_nil ().

ຄວາມແຕກຕ່າງອີກອັນ ໜຶ່ງ ທີ່ຕ້ອງໄດ້ເນັ້ນໃຫ້ເຫັນແມ່ນພຶດຕິ ກຳ ທີ່ບໍລິສຸດຂອງ ໜ້າ ທີ່ບ່ອນທີ່ Ecto. ເມື່ອໂທຫາບ່ອນທີ່ເຮັດວຽກຢູ່ບ່ອນດຽວ, ມັນບໍ່ພົວພັນກັບຖານຂໍ້ມູນ. ການກັບມາຂອງ ຕຳ ແໜ່ງ ບ່ອນທີ່ໂຄງສ້າງແມ່ນ Ecto.Query:

iex (20)> ບ່ອນທີ່ (ໃບເກັບເງິນ, [i], is_nil (i.paid_at))
# Ecto.Query <ຈາກ i ໃນ Financex.Accounts.Invoice, ບ່ອນທີ່: is_nil (i.paid_at)>

ຖານຂໍ້ມູນແມ່ນຖືກແຕະຕ້ອງເມື່ອຟັງຊັນ Repo.all () ຖືກເອີ້ນ, ຖ່າຍທອດໂຄງສ້າງ Ecto.Query ເປັນການໂຕ້ຖຽງ. ວິທີການນີ້ອະນຸຍາດໃຫ້ມີສ່ວນປະກອບການສອບຖາມໃນ Ecto, ເຊິ່ງແມ່ນຫົວເລື່ອງຂອງພາກຕໍ່ໄປ.

ສ່ວນປະກອບການສອບຖາມ

ໜຶ່ງ ໃນດ້ານທີ່ມີປະສິດທິພາບສູງສຸດຂອງການສອບຖາມຖານຂໍ້ມູນແມ່ນການປະກອບ. ມັນອະທິບາຍການສອບຖາມໃນແບບທີ່ມີຫຼາຍກວ່າສະພາບດຽວ.

ຖ້າທ່ານ ກຳ ລັງສ້າງແບບສອບຖາມ SQL ແບບດິບ, ມັນ ໝາຍ ຄວາມວ່າທ່ານອາດຈະໃຊ້ແບບສະຫຼຸບບາງປະເພດ. ຈິນຕະນາການວ່າທ່ານມີເງື່ອນໄຂສອງຢ່າງ:

  1. not_paid = 'ທີ່ຈ່າຍແລ້ວບໍ່ແມ່ນ'
  2. pay_with_paypal = 'payment_method = "Paypal"'

ເພື່ອສົມທົບສອງເງື່ອນໄຂດັ່ງກ່າວໂດຍໃຊ້ SQL ດິບ, ໝາຍ ຄວາມວ່າທ່ານຈະຕ້ອງສະຫຼຸບໃຫ້ພວກເຂົາໃຊ້ບາງສິ່ງທີ່ຄ້າຍຄືກັນກັບ:

ການຄັດເລືອກ * ຈາກໃບແຈ້ງ ໜີ້ ບ່ອນທີ່ # {not_paid} ແລະ # {pay_with_paypal}

ໂຊກດີທັງ ActiveRecord ແລະ Ecto ມີວິທີແກ້ໄຂ ສຳ ລັບເລື່ອງນັ້ນ.

ActiveRecord

irb (ຕົ້ນຕໍ): 003: 0> Invoice.where.not (pay_at: nil). ຢູ່ບ່ອນໃດ (pay_method: "Paypal") ໃບເກັບເງິນໃບເກັບເງິນ (8.0ms) ຄັດເລືອກ "ໃບເກັບເງິນ". pay_at "IS NULL ແລະ" ໃບເກັບເງິນ "." payment_method "= $ 1 ຈຳ ກັດ $ 2 [[" payment_method "," Paypal "], [" ຈຳ ກັດ ", 11]] => # ]>

Ecto

iex (6)> ໃບເກັບເງິນ |> ບ່ອນທີ່ ([i], ບໍ່ແມ່ນ is_nil (i.paid_at)) |> ບ່ອນທີ່ ([i], i.payment_method == "Paypal") |> Repo.all ()
[debug] QUERY OK source = "ໃບເກັບເງິນ" db = 30.0ms ຖອດລະຫັດ = 0.6ms ແຖວ = 0.2ms
SELECT i0. "id", i0. "payment_method", i0. "pay_at", i0. "user_id", i0. "insert_at", i0. "updates_at" FROM "ໃບແຈ້ງຫນີ້" AS i0 WHERE (ບໍ່ (i0 "ຈ່າຍ") "IS NULL)) ແລະ (i0." payment_method "= 'Paypal') []
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 2,
    insert_at: ~ N [2018-01-03 08: 00: 00.000000],
    pay_at: # ວັນທີ <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Paypal",
    update_at: ~ N [2018-01-03 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 2
  }
]

ປຽບທຽບ

ການສອບຖາມທັງສອງແມ່ນຕອບ ຄຳ ຖາມດຽວກັນ:“ ໃບແຈ້ງການໃດທີ່ໄດ້ຈ່າຍແລະໃຊ້ Paypal?”.

ດັ່ງທີ່ໄດ້ຄາດ ໝາຍ ໄວ້ແລ້ວ, ActiveRecord ສະ ເໜີ ວິທີການທີ່ ເໝາະ ສົມກວ່າໃນການປະກອບ ຄຳ ຖາມ (ຕົວຢ່າງນັ້ນ), ໃນຂະນະທີ່ Ecto ຮຽກຮ້ອງໃຫ້ນັກພັດທະນາໃຊ້ຈ່າຍເພີ່ມເຕີມໃນການຂຽນ ຄຳ ຖາມ. ຕາມປົກກະຕິ, Batgirl (ເດັກ ກຳ ພ້າ ກຳ ລັງສັບຊ້ອນກັບຕົວຕົນຂອງ Cassandra Cain) ຫຼື Activerecord ບໍ່ແມ່ນ ຄຳ ເວົ້າ.

ຢ່າຫລອກລວງດ້ວຍ ຄຳ ເວົ້າແລະຄວາມສັບສົນທີ່ປາກົດຂື້ນຂອງ ຄຳ ຖາມ Ecto ທີ່ສະແດງຢູ່ຂ້າງເທິງ. ໃນສະພາບແວດລ້ອມຂອງໂລກທີ່ແທ້ຈິງ, ການສອບຖາມນັ້ນຈະຖືກຂຽນຄືນເພື່ອເບິ່ງຄືວ່າ:

ໃບເກັບເງິນ
|> ບ່ອນທີ່ ([i], ບໍ່ແມ່ນ isnnil (i.paid_at))
|> ບ່ອນທີ່ ([i], i.payment_method == "Paypal")
|> Repo.all ()

ເບິ່ງຈາກມຸມນັ້ນ, ການປະສົມປະສານຂອງລັກສະນະ "ອັນບໍລິສຸດ" ຂອງ ໜ້າ ທີ່, ເຊິ່ງບໍ່ປະຕິບັດການປະຕິບັດງານຖານຂໍ້ມູນດ້ວຍຕົວມັນເອງ, ກັບຜູ້ປະກອບທໍ່, ເຮັດໃຫ້ສ່ວນປະກອບການສອບຖາມໃນ Ecto ສະອາດແທ້ໆ.

ກຳ ລັງສັ່ງ

ການຈັດລໍາດັບແມ່ນລັກສະນະທີ່ສໍາຄັນຂອງການສອບຖາມ. ມັນຊ່ວຍໃຫ້ນັກພັດທະນາຮັບປະກັນວ່າຜົນໄດ້ຮັບແບບສອບຖາມທີ່ປະຕິບັດຕາມ ຄຳ ສັ່ງທີ່ລະບຸ.

ActiveRecord

irb (ຕົ້ນຕໍ): 002: 0> Invoice.order (created_at:: desc) Invoice Load (1.5ms) SELECT "ໃບເກັບເງິນ". * FROM "ໃບແຈ້ງ ໜີ້" ສັ່ງໂດຍ "ໃບແຈ້ງ ໜີ້". "created_at" DESC ຈຳ ກັດ $ 1 [["ຈຳ ກັດ ", 11]] => # ]>

Ecto

iex (6)> order_by (ໃບເກັບເງິນ, desc:: inserted_at) |> Repo.all ()
[debug] QUERY OK source = "ໃບເກັບເງິນ" db = 19.8ms
SELECT i0. "id", i0. "payment_method", i0. "pay_at", i0. "user_id", i0. []
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 3,
    inserted_at: ~ N [2018-01-04 08: 00: 00.000000],
    pay_at: nil,
    payment_method: nil,
    update_at: ~ N [2018-01-04 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 3
  },,
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 4,
    inserted_at: ~ N [2018-01-04 08: 00: 00.000000],
    pay_at: nil,
    payment_method: nil,
    update_at: ~ N [2018-01-04 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 4
  },,
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 2,
    insert_at: ~ N [2018-01-03 08: 00: 00.000000],
    pay_at: # ວັນທີ <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Paypal",
    update_at: ~ N [2018-01-03 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 2
  },,
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 1,
    inserted_at: ~ N [2018-01-02 08: 00: 00.000000],
    pay_at: # ວັນທີ <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "ບັດເຄດິດ",
    update_at: ~ N [2018-01-02 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 1
  }
]

ປຽບທຽບ

ການເພີ່ມ ຄຳ ສັ່ງໃຫ້ກັບການສອບຖາມແມ່ນຖືກຕ້ອງໂດຍກົງໃນທັງສອງເຄື່ອງມື.

ເຖິງແມ່ນວ່າຕົວຢ່າງ Ecto ໃຊ້ Invoice ເປັນພາລາມິເຕີ ທຳ ອິດ, ໜ້າ ທີ່ order_by ຍັງຍອມຮັບໂຄງສ້າງຂອງ Ecto.Query ເຊິ່ງຊ່ວຍໃຫ້ຟັງຊັນສັ່ງ _ ສາມາດໃຊ້ໃນສ່ວນປະກອບຕ່າງໆເຊັ່ນ:

ໃບເກັບເງິນ
|> ບ່ອນທີ່ ([i], ບໍ່ແມ່ນ isnnil (i.paid_at))
|> ບ່ອນທີ່ ([i], i.payment_method == "Paypal")
|> order_by (desc:: inserted_at)
|> Repo.all ()

ຈຳ ກັດ

ຖານຂໍ້ມູນຈະເປັນແນວໃດໂດຍບໍ່ ຈຳ ກັດ? ໄພພິບັດ. ໂຊກດີ, ທັງ ActiveRecord ແລະ Ecto ຊ່ວຍໃນການ ຈຳ ກັດ ຈຳ ນວນບັນທຶກທີ່ຖືກສົ່ງຄືນ.

ActiveRecord

irb (ຕົ້ນຕໍ): 004: 0> Invoice.limit (2)
ໂຫຼດໃບເກັບເງິນ (0.2ms) ຄັດເລືອກ "ໃບເກັບເງິນ". * ຈາກ "ໃບເກັບເງິນ" ຈຳ ກັດ $ 1 [["ຈຳ ກັດ", 2]]
=> # , # ]>

Ecto

iex (22)> ຂໍ້ ຈຳ ກັດ (ໃບເກັບເງິນ, 2) |> Repo.all ()
[debug] QUERY OK source = "ໃບເກັບເງິນ" db = 3.6ms
SELECT i0. "id", i0. "payment_method", i0. "pay_at", i0. "user_id", i0.
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 1,
    insert_at: ~ N [2018-01-02 08: 00: 00.000000],
    pay_at: # ວັນທີ <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "ບັດເຄດິດ",
    update_at: ~ N [2018-01-02 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 1
  },,
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 2,
    insert_at: ~ N [2018-01-03 08: 00: 00.000000],
    pay_at: # ວັນທີ <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Paypal",
    update_at: ~ N [2018-01-03 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 2
  }
]

ປຽບທຽບ

ທັງ ActiveRecord ແລະ Ecto ມີວິທີການ ຈຳ ກັດ ຈຳ ນວນບັນທຶກທີ່ສົ່ງຄືນໂດຍການສອບຖາມ.

ຂອບເຂດຈໍາກັດຂອງ Ecto ເຮັດວຽກຄ້າຍຄືກັນກັບ order_by, ເໝາະ ສົມກັບສ່ວນປະກອບການສອບຖາມ.

ສະມາຄົມ

ActiveRecord ແລະ Ecto ມີວິທີການທີ່ແຕກຕ່າງກັນເມື່ອເວົ້າເຖິງການຈັດການກັບສະມາຄົມ.

ActiveRecord

ໃນ ActiveRecord, ທ່ານສາມາດໃຊ້ສະມາຄົມໃດ ໜຶ່ງ ທີ່ຖືກ ກຳ ນົດໄວ້ໃນແບບ ຈຳ ລອງ, ໂດຍບໍ່ຕ້ອງເຮັດຫຍັງພິເສດກ່ຽວກັບສິ່ງນັ້ນ, ຕົວຢ່າງ:

irb (ຕົ້ນຕໍ): 012: 0> user = User.find (2) User Load (0.3ms) SELECT "ຜູ້ໃຊ້". * ຈາກ "ຜູ້ໃຊ້" ບ່ອນທີ່ "ຜູ້ໃຊ້". "id" = $ 1 ຈຳ ກັດ $ 2 [["id" , 2], ["ຈຳ ກັດ", 1]] => # <ຊື່ຜູ້ໃຊ້: 2, full_name: "Barbara Gordon", ອີເມວ: "barbara@gordon.test", created_at: "2018-01-02 10:02: 00 ", updated_at:" 2018-01-02 10:02:00 "> irb (ຕົ້ນຕໍ): 013: 0> user.invoices Invoice Load (0.4ms) ຄັດເລືອກ" ໃບແຈ້ງ ໜີ້ ". * ຈາກ" ໃບເກັບເງິນ "ບ່ອນໃດທີ່" ໃບແຈ້ງ ໜີ້ " . "user_id" = $ 1 ຈຳ ກັດ $ 2 [["user_id", 2], ["ຈຳ ກັດ", 11]] => # ] >

ຕົວຢ່າງຂ້າງເທິງສະແດງໃຫ້ເຫັນວ່າພວກເຮົາສາມາດໄດ້ຮັບບັນຊີລາຍຊື່ຂອງໃບແຈ້ງຫນີ້ຂອງຜູ້ໃຊ້ໃນເວລາໂທຫາ user.invoices. ເມື່ອເຮັດດັ່ງນັ້ນ, ActiveRecord ໄດ້ສອບຖາມຖານຂໍ້ມູນໂດຍອັດຕະໂນມັດແລະໂຫລດໃບເກັບເງິນທີ່ມີສ່ວນພົວພັນກັບຜູ້ໃຊ້. ໃນຂະນະທີ່ວິທີການນີ້ເຮັດໃຫ້ສິ່ງຕ່າງໆງ່າຍຂື້ນ, ໃນແງ່ຂອງການຂຽນລະຫັດ ໜ້ອຍ ລົງຫຼືຕ້ອງກັງວົນກ່ຽວກັບຂັ້ນຕອນພິເສດ, ມັນອາດຈະເປັນປັນຫາຖ້າວ່າທ່ານ ກຳ ລັງກັງວົນກັບຜູ້ໃຊ້ ຈຳ ນວນ ໜຶ່ງ ແລະການເອີ້ນເກັບເງິນ ສຳ ລັບຜູ້ໃຊ້ແຕ່ລະຄົນ. ປະເດັນນີ້ເອີ້ນວ່າບັນຫາ N + 1.

ໃນ ActiveRecord, ການແກ້ໄຂທີ່ສະ ເໜີ ຕໍ່ບັນຫາ "N + 1" ແມ່ນການ ນຳ ໃຊ້ວິທີການລວມມີ:

irb (ຕົ້ນຕໍ): 022: 0> ຜູ້ໃຊ້ = User.includes (: ໃບແຈ້ງ ໜີ້) .find (2) Load User (0.3ms) SELECT "ຜູ້ໃຊ້". * ຈາກ "ຜູ້ໃຊ້" ບ່ອນທີ່ "ຜູ້ໃຊ້". "id" = $ 1 ຈຳ ກັດ $ 2 [["id", 2], ["ຈຳ ກັດ", 1]] ໃບເກັບເງິນໃບເກັບເງິນ (0.6ms) ເລືອກ "ໃບເກັບເງິນ". * ຈາກ "ໃບເກັບເງິນ" ບ່ອນໃດທີ່ "ໃບແຈ້ງ ໜີ້", "user_id" = $ 1 [["user_id", 2]] => # <ຜູ້ໃຊ້ id: 2, full_name: "Barbara Gordon", ອີເມວ: "barbara@gordon.test", created_at: "2018-01-02 10:02:00", update_at: "2018-01 -02 10:02:00 "> irb (ຕົ້ນຕໍ): 023: 0> user.invoices => # ]>

ໃນກໍລະນີນີ້, ActiveRecord ກະຕືລືລົ້ນທີ່ຈະໂຫຼດສະມາຄົມໃບເກັບເງິນໃນເວລາດຶງເອົາຜູ້ໃຊ້ (ດັ່ງທີ່ເຫັນໃນສອງແບບສອບຖາມ SQL ທີ່ສະແດງ).

Ecto

ຄືກັບທີ່ທ່ານອາດຈະໄດ້ສັງເກດເຫັນແລ້ວ, Ecto ບໍ່ມັກ magic ຫຼືມີຄວາມຮູ້ສຶກເປັນພິເສດ. ມັນຮຽກຮ້ອງໃຫ້ນັກພັດທະນາມີຄວາມລະອຽດກ່ຽວກັບເຈດ ຈຳ ນົງຂອງພວກເຂົາ.

ລອງໃຊ້ວິທີດຽວກັນກັບການໃຊ້ user.invoices ກັບ Ecto:

iex (7)> ​​ຜູ້ໃຊ້ = Repo.get (ຜູ້ໃຊ້, 2)
[debug] QUERY OK source = "ຜູ້ໃຊ້" db = 18.3ms ຖອດລະຫັດ = 0.6ms
SELECT u0. "id", u0. "full_name", u0. "ອີເມວ", u0. "ໃສ່_at", u0 "ອັບເດດ_at" ຜູ້ ນຳ ໃຊ້ "AS u0 WHERE (u0." id "= $ 1) [2]
% Financex.Accounts.User {
  __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ຜູ້ໃຊ້">,
  ອີເມວ: "barbara@gordon.test",
  full_name: "Barbara Gordon",
  id: 2,
  insert_at: ~ N [2018-01-02 10: 02: 00.000000],
  ໃບເກັບເງິນ: # Ecto.Association.NotLoaded ,
  update_at: ~ N [2018-01-02 10: 02: 00.000000]
}
iex (8)> user.invoices
# Ecto.Association.NotLoaded <ສະມາຄົມ: ໃບເກັບເງິນບໍ່ໄດ້ໂຫລດ>

ຜົນໄດ້ຮັບແມ່ນ Ecto.Association.NotLoaded. ບໍ່ມີປະໂຫຍດຫຍັງເລີຍ.

ເພື່ອໃຫ້ສາມາດເຂົ້າເຖິງໃບເກັບເງິນ, ນັກພັດທະນາຕ້ອງແຈ້ງໃຫ້ Ecto ຮູ້ກ່ຽວກັບເລື່ອງນັ້ນ, ໂດຍໃຊ້ຟັງຊັນ preload:

iex (12)> user = preload (User,: invoices) |> Repo.get (2)
[debug] QUERY OK source = "ຜູ້ໃຊ້" db = 11.8ms
SELECT u0. "id", u0. "full_name", u0. "ອີເມວ", u0. "ໃສ່_at", u0 "ອັບເດດ_at" ຜູ້ ນຳ ໃຊ້ "AS u0 WHERE (u0." id "= $ 1) [2]
[debug] QUERY OK source = "ໃບເກັບເງິນ" db = 4.2ms
SELECT i0. "id", i0. "payment_method", i0. "pay_at", i0. "user_id", i0. "inserted_at", i0. i0. "user_id" = $ 1) ສັ່ງໂດຍ i0. "user_id" [2]
% Financex.Accounts.User {
  __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ຜູ້ໃຊ້">,
  ອີເມວ: "barbara@gordon.test",
  full_name: "Barbara Gordon",
  id: 2,
  insert_at: ~ N [2018-01-02 10: 02: 00.000000],
  ໃບເກັບເງິນ: [
    % Financex.Accounts.Invoice {
      __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
      id: 2,
      insert_at: ~ N [2018-01-03 08: 00: 00.000000],
      pay_at: # ວັນທີ <2018-02-01 08: 00: 00.000000Z>,
      payment_method: "Paypal",
      update_at: ~ N [2018-01-03 08: 00: 00.000000],
      user: # Ecto.Association.NotLoaded ,
      user_id: 2
    }
  ],
  update_at: ~ N [2018-01-02 10: 02: 00.000000]
}

iex (15)> user.invoices
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: ໂຫລດ, "ໃບເກັບເງິນ">,
    id: 2,
    insert_at: ~ N [2018-01-03 08: 00: 00.000000],
    pay_at: # ວັນທີ <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Paypal",
    update_at: ~ N [2018-01-03 08: 00: 00.000000],
    user: # Ecto.Association.NotLoaded ,
    user_id: 2
  }
]

ຄ້າຍຄືກັນກັບ ActiveRecord ປະກອບມີ, ການຈັດການກັບການເກັບເອົາໃບແຈ້ງ ໜີ້ ທີ່ກ່ຽວຂ້ອງ, ເຊິ່ງຈະເຮັດໃຫ້ພວກເຂົາສາມາດໃຊ້ໄດ້ໃນເວລາໂທຫາ user.invoices.

ປຽບທຽບ

ອີກເທື່ອ ໜຶ່ງ, ການສູ້ຮົບລະຫວ່າງ ActiveRecord ແລະ Ecto ສິ້ນສຸດລົງດ້ວຍຈຸດທີ່ມີຊື່ສຽງ: ຜູ້ພິພາກສາ. ເຄື່ອງມືທັງສອງຊ່ວຍໃຫ້ນັກພັດທະນາສາມາດເຂົ້າເຖິງສະມາຄົມໄດ້ງ່າຍ, ແຕ່ໃນຂະນະທີ່ ActiveRecord ເຮັດໃຫ້ມັນບໍ່ມີຄວາມເວົ້າ, ຜົນຂອງມັນອາດຈະມີພຶດຕິ ກຳ ທີ່ບໍ່ຄາດຄິດ. Ecto ປະຕິບັດຕາມ WYSIWYG ປະເພດຂອງວິທີການ, ເຊິ່ງພຽງແຕ່ເຮັດສິ່ງທີ່ເຫັນໃນການສອບຖາມທີ່ກໍານົດໂດຍຜູ້ພັດທະນາ.

Rails ເປັນທີ່ຮູ້ຈັກກັນດີໃນການ ນຳ ໃຊ້ແລະສົ່ງເສີມຍຸດທະສາດໃນການຕັ້ງຄ່າໃນຖານຄວາມ ຈຳ ໃຫ້ກັບທຸກຊັ້ນຂໍ້ມູນທີ່ແຕກຕ່າງກັນຂອງການສະ ໝັກ. ຕົວຢ່າງ ໜຶ່ງ ແມ່ນກ່ຽວກັບການ ນຳ ໃຊ້ວິທີການໃນການຄົ້ນຄວ້າ "doll ລັດເຊຍ", ເຊິ່ງຂື້ນກັບທັງ ໝົດ ໃນບັນຫາ "N + 1" ສຳ ລັບກົນໄກໃນຖານຄວາມ ຈຳ ຂອງມັນໃນການປະຕິບັດວຽກຂອງມັນ.

ຄວາມຖືກຕ້ອງ

ຄວາມຖືກຕ້ອງສ່ວນໃຫຍ່ທີ່ມີຢູ່ໃນ ActiveRecord ຍັງມີຢູ່ໃນ Ecto. ຕໍ່ໄປນີ້ແມ່ນບັນຊີລາຍຊື່ຂອງຄວາມຖືກຕ້ອງທົ່ວໄປແລະວິທີການທັງ ActiveRecord ແລະ Ecto ກໍານົດພວກມັນ:

ສະ​ຫຼຸບ

ມີທ່ານມີມັນ: ຫມາກໂປມທີ່ຈໍາເປັນທຽບກັບການປຽບທຽບຫມາກກ້ຽງ.

ActiveRecord ສຸມໃສ່ຄວາມງ່າຍຂອງການປະຕິບັດການສອບຖາມຖານຂໍ້ມູນ. ຄຸນລັກສະນະສ່ວນໃຫຍ່ຂອງມັນແມ່ນສຸມໃສ່ຊັ້ນຮຽນແບບຕົວຂອງມັນເອງ, ບໍ່ຮຽກຮ້ອງໃຫ້ນັກພັດທະນາມີຄວາມເຂົ້າໃຈເລິກເຊິ່ງກ່ຽວກັບຖານຂໍ້ມູນ, ທັງຜົນກະທົບຈາກການ ດຳ ເນີນງານດັ່ງກ່າວ. ActiveRecord ເຮັດຫລາຍສິ່ງຫລາຍຢ່າງໂດຍໃນຕອນຕົ້ນ. ເຖິງແມ່ນວ່າສິ່ງນັ້ນຈະຊ່ວຍໃຫ້ການເລີ່ມຕົ້ນງ່າຍຂື້ນ, ມັນກໍ່ເຮັດໃຫ້ມັນຍາກທີ່ຈະເຂົ້າໃຈສິ່ງທີ່ ກຳ ລັງເກີດຂື້ນຫລັງເຫດການແລະມັນຈະເຮັດວຽກໄດ້ຖ້າທ່ານປະຕິບັດຕາມ“ ActiveRecord way”.

ໃນອີກດ້ານຫນຶ່ງ, Ecto ຮຽກຮ້ອງໃຫ້ມີຕົວຢ່າງທີ່ເປັນຜົນມາຈາກລະຫັດ verbose ຫຼາຍ. ເປັນຜົນປະໂຫຍດ, ທຸກສິ່ງທຸກຢ່າງແມ່ນຢູ່ໃນຈຸດເດັ່ນ, ບໍ່ມີຫຍັງຢູ່ເບື້ອງຫລັງ, ແລະທ່ານສາມາດລະບຸວິທີການຂອງທ່ານເອງ.

ທັງສອງມີແນວຄິດຂອງພວກເຂົາຂື້ນຢູ່ກັບທັດສະນະແລະຄວາມມັກຂອງທ່ານ. ດັ່ງນັ້ນໂດຍໄດ້ປຽບທຽບ ໝາກ ໂປມແລະ ໝາກ ກ້ຽງ, ພວກເຮົາມາຮອດຈຸດສຸດທ້າຍຂອງ BAT-tle ນີ້. ເກືອບລືມບອກທ່ານຊື່ລະຫັດຂອງ BatGirl (1989-2001) ແມ່ນ…. ໂອລາ. ແຕ່ຢ່າເຂົ້າໄປໃນເລື່ອງນັ້ນ.

ບົດຂຽນນີ້ຂຽນໂດຍນັກຂຽນແຂກ Elvio Vicosa. Elvio ແມ່ນຜູ້ຂຽນປື້ມ Phoenix for Rails Developers.

ລົງພິມໃນເບື້ອງຕົ້ນຢູ່ blog.appsignal.com ໃນວັນທີ 9 ຕຸລາ 2018.