سوال در مورد انواع macro join در الکسیر و همینطور تفاوت های آن ها

با درود خدمت دوستان محترم . یک چند وقتی هست من واقعا درگیر جزيیات زیادی در ecto شدم برای کار با بانک اطلاعاتی در الکسیر که در زبان های دیگه مثل php اینطور موارد وجود داشته ولی خوب همینطوری فقط به سیله من استفاده می شده نه اینکه انتخاب مناسبی شکل بگیره به هر صورت برسم به اصل مشکل :

در زمانی که سعی داشتم یک رلیشنی برای دوتا دیتابیسم درست کنم به قسمتی از داکیومنت Ecto برخورد کردم و همینطور ماکرو join که برای این ماکرو انواع مختلفی وجود داشت که آنچنان توضیحی در آن نفهته نبود و مجبور شدم در دیتابیس پستگرس یا مای اسکول برخی هاشو سرچ بزنم تا کمی متوجه بشم به عنوان مثال موارد زیر مطرح بود

:inner_join, :left_join, :right_join, :cross_join, :full_join, :inner_lateral_join or :left_lateral_join

به صورت مثال :

def right_join() do
		query = from p in UsersInfo,
          right_join: c in EditUserHistory, 
          where: c.user_id == p.id,
          where: p.age > 0,
          select: {p.full_name, c.old_email}
 
		Repo.all(query)
	end

	def join() do
		query = from p in UsersInfo,
          join: c in EditUserHistory, 
          where: c.user_id == p.id,
          where: p.age > 0,
          select: {p.full_name, c.old_email}
 
		Repo.all(query)
	end

	def left_join() do
		query = from p in UsersInfo,
          left_join: c in EditUserHistory, 
          where: c.user_id == p.id,
          where: p.age > 0,
          select: {p.full_name, c.old_email}
 
		Repo.all(query)
	end
  1. حال چند مشکل و سوال در این وسط پیش می یاد که هر کدوم از join ها دقیقا چه فرقی به زبان ساده می کنند ؟
  2. چرا سرعت دریافت اطلاعات در هر کدوم فرق می کند و چطور باید انتخاب گردد تا سرعت مناسبی برای دریافت اطلاعات شکل بگیرد ؟

در تصویر بالا من سه فانکشن رو تست زدم و هر کدوم سرعت خاص خودشون در دریافت اطلاعات داشتن .

برای اطلاعات بیشتر

من دو جدول دارم . یکی جدول کاربر و اطلاعات شخصیش هست و جدول دیگیری تاریخچه تغییرات کاربر می باشد .

در حقیقت بر اساس یک کاربر و کد uuid اون تاریخچه تغییرات آن باید فراخوانی و رلیشن شود

چهره شماتیک جداول

با تشکر از دوستان محترم

1 پسندیده

در اصل اینا سه جور جوین متفاوت هستند، که خیلی ربطی به php و‌elixir نداره.
یه نگاه به لینک زیر بنداز اگر مشکل حل نشد بیشتر توضیح میدم

https://www.w3schools.com/sql/sql_join.asp

درضمن میتونی با استفاده از to_sql کوئری ها رو به کد sql تبدیل کنی که گاهی خیلی بکار میاد. هرچند از توی لاگ هم میشه دید.

image

2 پسندیده

درود توماج جان . ممنون زا اون to_sql حتما مطالعه می کنم من این تصویر رو چندین ساله می بنیم ولی متاسفانه کامل متوجه نمی شم دقیقا چطور باید انتخابشون کنم .

مثلا دایره left join زمانی استفاده می شه که که بیشترین اطلاعاتی ما در سمت چپ هست و ممکنه از یک عنصر جدول راست هم استفاده کنیم

و right join هم همینطور ؟ و inner یعنی چی؟ به زبان ساده اگر وقت بکنید یک توضیح کوتاهی بدید ممنون می شم . و چرا روی سرعت تاثیر گزاشته در موقع خوندن عکس ترمینال در پست اول رو لطفا ببنید هر کدوم سرعت فراخوانی در اون نوشته شده

سوال اول و اصلی اینکه در چه زمانی کدام را انتخاب می کنید؟

2 پسندیده

در ۹۰ درصد query ها از join یا همون inner join استفاده میشه که داده مشترک بین دو جدول نشون میده
برای درک کامل بقیه با داده بهتر معلوم تفاوت شون

3 پسندیده

بسیار ممنون سام عزیز مثال هاش مخصوصا اینکه گرافیکی آورده بود بسیار ارزشمند بود .
فقط سوالی دیگه ای که دارم اینکه آیا در سرعت فراخوانی داده تفاوتی دارد یا خیر؟

در بیشتر موارد INNER JOIN یا همون JOIN معمول از همه سریع تره LEFT JOIN و… داده های بیشتری باید آنالیز کنه اصولا کند تره

3 پسندیده

در بیشتر مواقع inner join رکوردهای کمتری رو fetch میکنه که کاربردی تره.

3 پسندیده

دوستان من قبلا در مورد ecto مطالعه داشتم فکر کنم جلوی باگ های اینجکشن می گیره درسته ؟

این حرف درسته ؟

Queries are sanitized and protected against SQL Injection

اینی که پست کردی مربوط به جلوگیری از اجرای کوئری ناخواستست.
مثال زیر خیلی خوب نیست اما برای شروع بد نیست؛
فکر یک فانکشن خیالی داریک‌که یک کوئری دانامیک اجرا میکنه و اون کوئری q هست

 def(q) do
  Ecto.Adapters.SQL.query(Repo, q)
 end

اگر به هر دلیلی کسی بتونه به q کنترل داشته باشه، مثلا اگر q بر اساس درخواست در یک کنترلر bind بشه, امکانش هست که هر کد sql ی رو اجرا کرد

1 پسندیده

توماج جان مثل اینکه اون متن اشتباه گزاشتم ولی خوب شد این کار ناخواسته باعث شد چیزی رو یاد بگیرم . ولی حالا سوالم بالا آیا اکتو جلوی اسکوال اینجکشن می گیره یا نه؟

ممنون ازت

راستش من ترجیح میدم تصور کنم که نمیگیره و‌مواظب raw sql ها باشم

2 پسندیده

میتونی اصلا تست کنی، یک پارامتر به پامتر های درخواست بده مثلا به نام user_q, بعد با روشی که عرض کردم سعی کن یک کد sql با استفاده از user_q بفرستی که بوسیله Ecto.Adapters.SQL.query اجرا بشه، بعد ببین چه اتفاقی میوفته، من همیشه جلوی این موردو میگیرم برای همین تا حلا به اینکه اکتو چه کمکی ممکنه بکنه فکر نکرده بودم.

اگر نتیجه رو هم بگی کمک بزرگیه :love_you_gesture:

1 پسندیده

توماج جان من دقیقا متوجه نشدم مثلا چیجوری تو کدم باگ درست کنم مثلا کد زیر رو نگاه کنید لطفا

	def join(age) do
		query = from p in UsersInfo,
          join: c in EditUserHistory, 
          where: c.users_info_id == p.id,
          where: p.age > ^age,
          select: %{full_name: p.full_name, email: c.old_email}
 
		Repo.all(query)
	end

ورودی من age هست که من اصلا در کد زیر چک نمی کنم آیا عدد هست یا هیچی ولی باید عدد باشه اینجا چیجوری می شه باگ ایجاد کرد تا ببنیم اکتو مراقبت می کنه یا نه ؟

اینم از داکیومنت اکتو گرفتم

Ecto.Query - written in Elixir syntax, queries are used to retrieve information from a given repository. Queries in Ecto are secure, avoiding common problems like SQL Injection, while still being composable, allowing developers to build queries piece by piece instead of all at once

داکیومنت بالا
https://hexdocs.pm/ecto/Ecto.html

یه مثال بهتر میزنم، فقط اگر همیشه لینک داکیومنت رو هم بدی بهتره

توماج جان اینو ببین

این برگشت repo.all هست وقتی کد بالا من عدد نباشه

مثلا این کد رو ببنید

	def join(age) do
		query = from p in UsersInfo,
          join: c in EditUserHistory, 
          where: c.users_info_id == p.id,
          where: p.age > ^age,
          select: %{full_name: p.full_name, email: c.old_email}
 
		Repo.all(query)
	end

وقتی age ما عدد نباشه چنین اروری در ترمینال می ده که عکسشو گزاشتم . حالا مشکل این هست که من نمی تونم با پترن بگیرمش
https://hexdocs.pm/ecto/Ecto.Repo.html#c:all/2

پست بالا هم لینک داکیومنتشو قرار دادم

1 پسندیده

این لینک رو بخون شهریارجان
1 پسندیده

توماج جان البته با گارد می شه جلوشو گرفت

	def join(age) when is_number(age) do
		query = from p in UsersInfo,
          join: c in EditUserHistory, 
          where: c.users_info_id == p.id,
          where: p.age > ^age,
          select: %{full_name: p.full_name, email: c.old_email}
 
		Repo.all(query)
	end

ولی خوب هنوز اون حالت تست هنوز نتونستم . خودت می تونی یک تست بزنی با اینکه اون منبع بالا رو هم ویرایش کردم شما بیشتر به این حملات آشنایید شاید بتونید کامل تستش کنید من که می زنم تو ترمینال ارور دریافت می کنم

به روز رسانی

اینجا در مورد چطور گرفتن ارور سوال کردم که بهم پاسخ دادن دمشون گرم

کد تغییر کرد به این مورد


	def join1(age) do
		query = from p in UsersInfo,
          join: c in EditUserHistory, 
          where: c.users_info_id == p.id,
          where: p.age > ^age,
          select: %{full_name: p.full_name, email: c.old_email}
 
		Repo.all(query)
		rescue
  			e in Ecto.Query.CastError -> {:error, :cast_error}
		else
  		results -> {:ok, results}
	end

مشکل اینکه من کواری نمی زنم که دارم از کد های داخل خود الکسیر استفاده می کنم

1 پسندیده

مشکل casting هست اما کدوم قسمت کد integer نیست؟

1 پسندیده

من نگفتم اصلا مشکلی وجود داره، بحث اینجکشن بود که شما مطرح کرده بودین منم توضیح دادم که چطور ممکنه بوجود بیاد. اگر از متد های ecto استفاده کنی چون دیگه با raw query سر و کار نداری مشکل اینجکشن هم وجود نداره. اما احتمالا روشی که من گفتم قابل بازسازی باشه حالا شاید با کمی تغییر در کدی که دادم، اون کد فقط یک مثال نامناسب بود که البته عرض هم کردم مثال خوبی نیست.

در مورد گارد هم بیشتر توضیح بده، گارد یک روش کنترلی هست اما رابطه مستقیمی با ecto یا اینجکشن نداره، البته اگر هر دو منظورمون گاردکلاوز باشه.

2 پسندیده