sql >> Database teknologi >  >> RDS >> Mysql

Ecto-forespørgsel og tilpasset MySQL-funktion med variabel aritet

ORM er vidunderlige, indtil de lækker . Det gør alle til sidst. Ecto er ung (dvs. den fik kun evnen til at OR hvor klausuler sammen 30 dage siden ), så det er simpelthen ikke modent nok til at have udviklet en API, der tager højde for avancerede SQL-gyrationer.

Når du undersøger mulige muligheder, er du ikke alene i anmodningen. Manglende evne til at forstå lister i fragmenter (enten som en del af order_by eller where eller et hvilket som helst andet sted) er blevet nævnt i Ecto-udgave #1485 , på StackOverflow , på Elixir Forum og dette blogindlæg . Det sidste er særligt lærerigt. Mere om det om lidt. Lad os først prøve nogle eksperimenter.

Eksperiment #1: Man kan måske først prøve at bruge Kernel.apply/3 for at videregive listen til fragment , men det virker ikke:

|> order_by(Kernel.apply(Ecto.Query.Builder, :fragment, ^ids))

Eksperiment #2: Så kan vi måske bygge det med strengmanipulation. Hvad med at give fragment en streng bygget ved kørsel med nok pladsholdere til at den kan trækkes fra listen:

|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(Enum.map(ids, fn _ -> "?" end), ","), ")"], ""), ^ids))

Hvilket ville producere FIELD(id,?,?,?) givet ids = [1, 2, 3] . Nej, det virker heller ikke.

Eksperiment #3: Oprettelse af hele den endelige SQL bygget fra id'erne, placerer de rå ID-værdier direkte i den sammensatte streng. Udover at det er forfærdeligt, så virker det heller ikke:

|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(^ids, ","), ")"], "")))

Eksperiment #4: Dette bringer mig rundt til det blogindlæg, jeg nævnte. I den hacker forfatteren uden om manglen på or_where ved hjælp af et sæt foruddefinerede makroer baseret på antallet af betingelser, der skal samles:

defp orderby_fragment(query, [v1]) do
  from u in query, order_by: fragment("FIELD(id,?)", ^v1)
end
defp orderby_fragment(query, [v1,v2]) do
  from u in query, order_by: fragment("FIELD(id,?,?)", ^v1, ^v2)
end
defp orderby_fragment(query, [v1,v2,v3]) do
  from u in query, order_by: fragment("FIELD(id,?,?,?)", ^v1, ^v2, ^v3)
end
defp orderby_fragment(query, [v1,v2,v3,v4]) do
  from u in query, order_by: fragment("FIELD(id,?,?,?)", ^v1, ^v2, ^v3, ^v4)
end

Selvom dette virker og bruger ORM "med kornet" så at sige, kræver det, at du har et begrænset, overskueligt antal tilgængelige felter. Dette kan være en game changer eller ikke.

Min anbefaling:Forsøg ikke at jonglere uden om en ORMs lækager. Du kender den bedste forespørgsel. Hvis ORM ikke vil acceptere det, skriv det direkte med rå SQL, og dokumenter hvorfor ORM ikke virker. Beskyt den bag en funktion eller et modul, så du kan forbeholde dig den fremtidige ret til at ændre implementeringen. En dag, når ORM'en indhenter det, kan du så bare omskrive det pænt uden at påvirke resten af ​​systemet.



  1. Fejl under indlæsning af MySQLdb-modul og pip-installation af MySQLdb

  2. Hvordan laver man en massedatabaseindsættelse i Yii2?

  3. Sådan aktiveres/deaktiveres CHECK-begrænsninger i SQLite

  4. Gå til start testdrevet databaseudvikling (TDDD)