it-swarm.asia

SQL JOIN وأنواع مختلفة من JOINs

ما هو SQL JOIN وما هي الأنواع المختلفة؟

225
M-D

ما هو SQL JOIN؟

SQL JOIN هي طريقة لاسترداد البيانات من جدولين أو أكثر من جداول قاعدة البيانات.

ما هي SQL JOINs المختلفة؟

هناك ما مجموعه خمسة JOINs. هم انهم :

  1. JOIN or INNER JOIN
  2. OUTER JOIN

     2.1 LEFT OUTER JOIN or LEFT JOIN
     2.2 RIGHT OUTER JOIN or RIGHT JOIN
     2.3 FULL OUTER JOIN or FULL JOIN

  3. NATURAL JOIN
  4. CROSS JOIN
  5. SELF JOIN

1. انضم أو انضم إلى:

في هذا النوع من JOIN ، نحصل على جميع السجلات التي تطابق الشرط في كلا الجدولين ، ولا يتم الإبلاغ عن السجلات في كلا الجدولين غير المطابقتين.

بمعنى آخر ، يعتمد INNER JOIN على حقيقة واحدة هي: يجب فقط إدراج الإدخالات المتطابقة في كلا الجدولين.

لاحظ أن JOIN بدون أي JOIN- كلمات رئيسية أخرى (مثل INNER ، OUTER ، LEFT ، إلخ) INNER JOIN. بمعنى آخر ، JOIN هو سكر نحوي لـ INNER JOIN (راجع: الفرق بين JOIN و INNER JOIN ).

2. الانضمام الخارجي:

OUTER JOIN يسترد

إما ، الصفوف المتطابقة من جدول وكل الصفوف في الجدول الآخر أو ، كل الصفوف في جميع الجداول (لا يهم ما إذا كان هناك تطابق أم لا).

هناك ثلاثة أنواع من الانضمام الخارجي:

2.1 الانضمام إلى اليسار أو الانضمام إلى اليسار

تقوم هذه الصلة بإرجاع كافة الصفوف من الجدول الأيسر بالاقتران مع الصفوف المتطابقة من الجدول الأيمن. إذا لم تكن هناك أعمدة مطابقة في الجدول الأيمن ، فتُرجع قيم NULL.

2.2 الانضمام إلى اليمين أو الانضمام إلى اليمين

هذا JOIN بإرجاع كافة الصفوف من الجدول الأيمن بالاقتران مع الصفوف المطابقة من الجدول الأيسر. إذا لم تكن هناك أعمدة مطابقة في الجدول الأيسر ، فتُرجع قيم NULL.

2.3 FULL OINTER JOIN أو FULL JOIN

يجمع هذا JOIN بين LEFT OUTER JOIN و RIGHT OUTER JOIN. تقوم بإرجاع صفوف من أي جدول عند استيفاء الشروط وإرجاع قيمة NULL عند عدم وجود تطابق.

بمعنى آخر ، يعتمد OUTER JOIN على حقيقة أن: يتم إدراج الإدخالات المتطابقة فقط في ONE من الجداول (RIGHT أو LEFT) أو كلاهما من الجداول (FULL).

Note that `OUTER JOIN` is a loosened form of `INNER JOIN`.

3. الانضمام الطبيعي:

يعتمد على الشرطين:

  1. يتم تكوين JOIN في جميع الأعمدة بنفس الاسم للمساواة.
  2. يزيل الأعمدة المكررة من النتيجة.

يبدو أن هذا أكثر نظريًا في الطبيعة ونتيجة لذلك (ربما) لا يزعج معظم نظم إدارة قواعد البيانات بدعم هذا.

4. عبر الانضمام:

إنه المنتج الديكارتي للجدولين المعنيين. نتيجة CROSS JOIN لن يكون لها معنى في معظم الحالات. علاوة على ذلك ، لن نحتاج إلى ذلك على الإطلاق (أو نحتاج إلى الدقة على الأقل).

5. انضم إلينا:

إنه ليس شكل مختلف من JOIN ، بل هو JOIN (INNER ، OUTER ، إلخ) للجدول لنفسه.

ينضم على أساس المشغلين

حسب المشغل المستخدم في جملة JOIN ، يمكن أن يكون هناك نوعان من JOINs. هم انهم

  1. Equi JOIN
  2. ثيتا جوين

1. Equi JOIN:

مهما كان نوع JOIN (INNER ، OUTER ، إلخ) ، إذا استخدمنا عامل المساواة فقط (=) ، فنحن نقول أن JOIN هو EQUI JOIN.

2. ثيتا جوين:

هذا مماثل لـ EQUI JOIN ولكنه يسمح لجميع المشغلين الآخرين مثل> ، <،> = إلخ.

ينظر الكثيرون إلى EQUI JOIN و Theta JOIN على غرار INNER و OUTER etc JOINs. لكنني أؤمن بشدة أن هذا خطأ ويجعل الأفكار غامضة. نظرًا لأن INNER JOIN و OUTER JOIN وغيرها جميعها مرتبطة بالجداول وبياناتها في حين أن EQUI JOIN و THETA JOIN مرتبطان فقط بالمشغلين الذين نستخدمهما في السابق.

مرة أخرى ، هناك الكثير ممن يعتبرون NATURAL JOIN بمثابة نوع من أنواع "غريبة" EQUI JOIN. في الحقيقة ، صحيح ، بسبب الشرط الأول الذي ذكرته لـ NATURAL JOIN. ومع ذلك ، لا يتعين علينا تقييد ذلك ببساطة بـ NATURAL JOINs وحدها. INNER JOINs ، OUTER JOINs وما إلى ذلك يمكن أن يكون EQUI JOIN أيضًا.

236
M-D

رسم توضيحي من مدارس W3 :


INNER JOIN - Only records which match the condition in both tables


LEFT JOIN - All records from table 1 in conjunction with records which match the condition in table 2


RIGHT JOIN - All records from table 2 in conjunction with records from table 1 which match the condition


FULL OUTER JOIN - Combination of both Left and Right Outer joins matching ON clause but preserving both tables


319
Anup

التعريف:


JOINS هي وسيلة للاستعلام عن البيانات المدمجة معًا من جداول متعددة في وقت واحد.

أنواع JOINS:


فيما يتعلق بـ RDBMS ، هناك 5 أنواع من الصلات:

  • Equi-Join: يجمع السجلات المشتركة من جدولين على أساس شرط المساواة. من الناحية الفنية ، تم الانضمام من خلال استخدام المساواة-عامل (=) لمقارنة قيم المفتاح الأساسي لجدول واحد وقيم المفتاح الخارجي لجدول آخر ، وبالتالي تتضمن مجموعة النتائج سجلات مشتركة (مطابقة) من كلا الجدولين. للتنفيذ ، انظر INNER-JOIN.

  • Natural-Join: إنه إصدار محسّن من Equi-Join ، حيث تتجاهل عملية SELECT العمود المكرر. للتنفيذ ، انظر INNER-JOIN

  • Non-Equi-Join: إنه عكس Equi-join حيث يستخدم شرط الانضمام بخلاف عامل التشغيل المتساوي (=) على سبيل المثال ،! = ، <= ،> = ،> ، <أو بين إلخ. For تنفيذ انظر INNER-JOIN.

  • الانضمام الذاتي: : سلوك مخصص للانضمام حيث يتم دمج جدول مع نفسه ؛ هذا مطلوب عادةً للاستعلام عن جداول المراجع الذاتية (أو كيان علاقة Unary). للتنفيذ ، انظر INNER-JOINs.

  • المنتج الديكارتي: يتقاطع مع كل سجلات كلا الجدولين دون أي شرط. من الناحية الفنية ، تقوم بإرجاع مجموعة نتائج استعلام دون جملة WHERE.

حسب اهتمامات SQL والتقدم ، هناك 3 أنواع من الصلات ويمكن تحقيق جميع صلات RDBMS باستخدام هذه الأنواع من الصلات.

  1. INNER-JOIN: يدمج (أو يجمع) صفوف متطابقة من جدولين. تتم المطابقة بناءً على أعمدة الجداول الشائعة وعملية المقارنة الخاصة بها. إذا كان الشرط قائمًا على المساواة ، فقم بإجراء EQUI-JOIN ، خلاف ذلك Non-EQUI-Join.

  2. ** OUTER-JOIN: ** يدمج (أو يجمع) الصفوف المتطابقة من جدولين وصفوف لا مثيل لها مع قيم NULL. ومع ذلك ، يمكن تخصيص التحديد للصفوف غير المتطابقة ، على سبيل المثال ، تحديد صف لا مثيل له من الجدول الأول أو الجدول الثاني حسب الأنواع الفرعية: LEFT OUTER JOIN و RIGHT OUTER JOIN.

    2.1. LEFT Outer JOIN (a.k.a ، LEFT-JOIN): تشكل الصفوف المتطابقة جدولين وغير متطابقين من جدول LEFT (أي ، الجدول الأول) فقط.

    2.2. RIGHT Outer JOIN (a.k.a ، RIGHT-JOIN): إرجاع الصفوف المتطابقة من جدولين وغير متطابقين من جدول RIGHT فقط.

    2.3. FULL OUTER JOIN (a.k.a OUTER JOIN): إرجاع مطابقة وغير متطابقة من كلا الجدولين.

  3. CROSS-JOIN: هذا الربط لا يدمج/يجمع بدلاً من ذلك أنه ينفذ منتج الديكارتية.

enter image description here ملاحظة: يمكن تحقيق الانضمام الذاتي عن طريق إما INNER-JOIN و OUTER-JOIN و CROSS-JOIN بناءً على المتطلبات ولكن يجب أن ينضم الجدول إلى نفسه.

لمزيد من المعلومات:

أمثلة:

1.1: INNER-JOIN: تطبيق Equi-join

SELECT  *
FROM Table1 A 
 INNER JOIN Table2 B ON A.<Primary-Key> =B.<Foreign-Key>;

1.2: INNER-JOIN: تطبيق Natural-JOIN

Select A.*, B.Col1, B.Col2          --But no B.ForeignKeyColumn in Select
 FROM Table1 A
 INNER JOIN Table2 B On A.Pk = B.Fk;

1.3: INNER-JOIN مع تطبيق NON-Equi-join

Select *
 FROM Table1 A INNER JOIN Table2 B On A.Pk <= B.Fk;

1.4: INNER-JOIN مع SELF-JOIN

Select *
 FROM Table1 A1 INNER JOIN Table1 A2 On A1.Pk = A2.Fk;

2.1: الانضمام الخارجي (صلة خارجية كاملة)

Select *
 FROM Table1 A FULL OUTER JOIN Table2 B On A.Pk = B.Fk;

2.2: LEFT JOIN

Select *
 FROM Table1 A LEFT OUTER JOIN Table2 B On A.Pk = B.Fk;

2.3: الانضمام الصحيح

Select *
 FROM Table1 A RIGHT OUTER JOIN Table2 B On A.Pk = B.Fk;

3.1: CROSS JOIN

Select *
 FROM TableA CROSS JOIN TableB;

3.2: CROSS JOIN-Self JOIN

Select *
 FROM Table1 A1 CROSS JOIN Table1 A2;

//أو//

Select *
 FROM Table1 A1,Table1 A2;
64
nayeemDotNetAuthorities

ومن المثير للاهتمام أن معظم الإجابات الأخرى تعاني من هاتين المشكلتين:

لقد كتبت مؤخرًا مقالًا حول هذا الموضوع: دليل غير مكتمل ربما ، شامل للعديد من الطرق المختلفة لجداول JOIN في SQL ، والتي سأوجزها هنا.

أولاً وقبل كل شيء: JOINs هي منتجات ديكارتية

هذا هو السبب في أن مخططات Venn تشرحها بشكل غير دقيق ، لأن JOIN تقوم بإنشاء منتج ديكارت بين الجدولين المرتبطين. يوضح ويكيبيديا ذلك بشكل جيد:

 enter image description here

بناء جملة SQL للمنتجات الديكارتية هو CROSS JOIN. فمثلا:

SELECT *

-- This just generates all the days in January 2017
FROM generate_series(
  '2017-01-01'::TIMESTAMP,
  '2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
  INTERVAL '1 day'
) AS days(day)

-- Here, we're combining all days with all departments
CROSS JOIN departments

الذي يجمع كل الصفوف من جدول واحد مع جميع الصفوف من الجدول الآخر:

مصدر:

+--------+   +------------+
| day    |   | department |
+--------+   +------------+
| Jan 01 |   | Dept 1     |
| Jan 02 |   | Dept 2     |
| ...    |   | Dept 3     |
| Jan 30 |   +------------+
| Jan 31 |
+--------+

نتيجة:

+--------+------------+
| day    | department |
+--------+------------+
| Jan 01 | Dept 1     |
| Jan 01 | Dept 2     |
| Jan 01 | Dept 3     |
| Jan 02 | Dept 1     |
| Jan 02 | Dept 2     |
| Jan 02 | Dept 3     |
| ...    | ...        |
| Jan 31 | Dept 1     |
| Jan 31 | Dept 2     |
| Jan 31 | Dept 3     |
+--------+------------+

إذا كتبنا للتو قائمة من الجداول المفصولة بفواصل ، فسنحصل على ما يلي:

-- CROSS JOINing two tables:
SELECT * FROM table1, table2

إنر جوين (ثيتا جوين)

INNER JOIN هو مجرد CROSS JOIN مُفلتر حيث يُطلق على مسند المرشح Theta في الجبر العلائقي.

على سبيل المثال:

SELECT *

-- Same as before
FROM generate_series(
  '2017-01-01'::TIMESTAMP,
  '2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
  INTERVAL '1 day'
) AS days(day)

-- Now, exclude all days/departments combinations for
-- days before the department was created
JOIN departments AS d ON day >= d.created_at

لاحظ أن الكلمة الأساسية INNER اختيارية (باستثناء في MS Access).

( انظر إلى المقال للحصول على أمثلة للنتائج )

EQUI JOIN

نوع خاص من Theta-JOIN هو equi JOIN ، الذي نستخدمه أكثر من غيره. تربط المسند المفتاح الرئيسي لجدول ما بالمفتاح الخارجي لجدول آخر. إذا استخدمنا Sakila قاعدة البيانات للتوضيح ، يمكننا كتابة:

SELECT *
FROM actor AS a
JOIN film_actor AS fa ON a.actor_id = fa.actor_id
JOIN film AS f ON f.film_id = fa.film_id

هذا يجمع بين جميع الجهات الفاعلة مع أفلامهم.

أو أيضًا ، في بعض قواعد البيانات:

SELECT *
FROM actor
JOIN film_actor USING (actor_id)
JOIN film USING (film_id)

يسمح بناء جملة USING() بتحديد عمود يجب أن يكون موجودًا على جانبي جداول عملية JOIN ويقوم بإنشاء مساواة مسندة على هذين العمودين.

الانضمام الطبيعي

أوردت إجابات أخرى هذا النوع "JOIN type" بشكل منفصل ، لكن هذا غير منطقي. إنه مجرد شكل من أشكال بناء جملة السكر لـ equi JOIN ، وهي حالة خاصة من Theta-JOIN أو INNER JOIN. يقوم NATURAL JOIN ببساطة بتجميع الكل الأعمدة الشائعة في كلا الجدولين الذين يتم ضمهم وينضم USING() إلى هذه الأعمدة. وهو أمر غير مفيد على الإطلاق ، بسبب التطابقات العرضية (مثل أعمدة LAST_UPDATE في قاعدة بيانات Sakila ).

وفيما يلي بناء الجملة:

SELECT *
FROM actor
NATURAL JOIN film_actor
NATURAL JOIN film

الانضمام إلى الخارج

الآن ، OUTER JOIN يختلف قليلاً عن INNER JOIN لأنه ينشئ UNION من العديد من المنتجات الديكارتية. يمكننا الكتابة:

-- Convenient syntax:
SELECT *
FROM a LEFT JOIN b ON <predicate>

-- Cumbersome, equivalent syntax:
SELECT a.*, b.*
FROM a JOIN b ON <predicate>
UNION ALL
SELECT a.*, NULL, NULL, ..., NULL
FROM a
WHERE NOT EXISTS (
  SELECT * FROM b WHERE <predicate>
)

لا أحد يريد كتابة هذا الأخير ، لذلك نكتب OUTER JOIN (والذي عادة ما يكون أفضل بواسطة قواعد البيانات).

مثل INNER ، الكلمة الأساسية OUTER اختيارية ، هنا.

OUTER JOIN يأتي بثلاث نكهات:

  • LEFT [ OUTER ] JOIN: يضاف الجدول الأيسر للتعبير JOIN إلى الاتحاد كما هو موضح أعلاه.
  • RIGHT [ OUTER ] JOIN: تتم إضافة الجدول الأيمن للتعبير JOIN إلى الاتحاد كما هو موضح أعلاه.
  • FULL [ OUTER ] JOIN: تتم إضافة كلا الجدولين من التعبير JOIN إلى الاتحاد كما هو موضح أعلاه.

كل هذه العناصر يمكن دمجها مع الكلمة الأساسية USING() أو NATURAL ( لقد كان لدي بالفعل حالة استخدام في العالم الحقيقي لرمز NATURAL FULL JOIN مؤخرًا )

بناء الجملة البديل

هناك بعض الجمل التاريخية المهملة في Oracle و SQL Server ، والتي دعمت OUTER JOIN بالفعل قبل أن يحتوي معيار SQL على بناء جملة لهذا:

-- Oracle
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id = fa.actor_id(+)
AND fa.film_id = f.film_id(+)

-- SQL Server
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id *= fa.actor_id
AND fa.film_id *= f.film_id

بعد قولي هذا ، لا تستخدم بناء الجملة هذا. أنا فقط أسرد هذا هنا حتى تتمكن من التعرف عليه من مشاركات المدونات القديمة/الكود القديم.

مفصول OUTER JOIN

قلة من الناس يعرفون ذلك ، لكن معيار SQL يحدد OUTER JOIN المقسم (ويطبقه Oracle). يمكنك كتابة أشياء مثل هذا:

WITH

  -- Using CONNECT BY to generate all dates in January
  days(day) AS (
    SELECT DATE '2017-01-01' + LEVEL - 1
    FROM dual
    CONNECT BY LEVEL <= 31
  ),

  -- Our departments
  departments(department, created_at) AS (
    SELECT 'Dept 1', DATE '2017-01-10' FROM dual UNION ALL
    SELECT 'Dept 2', DATE '2017-01-11' FROM dual UNION ALL
    SELECT 'Dept 3', DATE '2017-01-12' FROM dual UNION ALL
    SELECT 'Dept 4', DATE '2017-04-01' FROM dual UNION ALL
    SELECT 'Dept 5', DATE '2017-04-02' FROM dual
  )
SELECT *
FROM days 
LEFT JOIN departments 
  PARTITION BY (department) -- This is where the magic happens
  ON day >= created_at

أجزاء من النتيجة:

+--------+------------+------------+
| day    | department | created_at |
+--------+------------+------------+
| Jan 01 | Dept 1     |            | -- Didn't match, but still get row
| Jan 02 | Dept 1     |            | -- Didn't match, but still get row
| ...    | Dept 1     |            | -- Didn't match, but still get row
| Jan 09 | Dept 1     |            | -- Didn't match, but still get row
| Jan 10 | Dept 1     | Jan 10     | -- Matches, so get join result
| Jan 11 | Dept 1     | Jan 10     | -- Matches, so get join result
| Jan 12 | Dept 1     | Jan 10     | -- Matches, so get join result
| ...    | Dept 1     | Jan 10     | -- Matches, so get join result
| Jan 31 | Dept 1     | Jan 10     | -- Matches, so get join result

النقطة المهمة هنا هي أن جميع الصفوف من الجانب المقسم من الصلة ستنتهي في النتيجة بغض النظر عما إذا كان JOIN يطابق أي شيء على "الجانب الآخر من JOIN". قصة قصيرة طويلة: هذا هو لملء البيانات المتفرقة في التقارير. مفيد جدا!

SEMI JOIN

بشكل جاد؟ لا إجابة أخرى حصلت على هذا؟ بالطبع لا ، لأنه لا يحتوي على بناء جملة أصلي في SQL ، للأسف (تمامًا مثل ANTI JOIN أدناه). ولكن يمكننا استخدام IN() و EXISTS() ، على سبيل المثال للعثور على جميع الممثلين الذين لعبوا في الأفلام:

SELECT *
FROM actor a
WHERE EXISTS (
  SELECT * FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)

تعمل WHERE a.actor_id = fa.actor_id المسند كمسند شبه صلة. إذا كنت لا تصدق ذلك ، فراجع خطط التنفيذ ، على سبيل المثال في أوراكل. سترى أن قاعدة البيانات تنفذ عملية SEMI JOIN ، وليس دالة EXISTS().

 enter image description here

مكافحة الانضمام

هذا هو عكس SEMI JOIN ( احرص على عدم استخدام NOT IN رغم ذلك ، نظرًا لأنه يحتوي على تحذير هام)

إليكم جميع الممثلين بدون أفلام:

SELECT *
FROM actor a
WHERE NOT EXISTS (
  SELECT * FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)

يكتب بعض الأشخاص (خاصةً أفراد MySQL) ANTI JOIN مثل هذا:

SELECT *
FROM actor a
LEFT JOIN film_actor fa
USING (actor_id)
WHERE film_id IS NULL

أعتقد أن السبب التاريخي هو الأداء.

الانضمام اللاحق

كاكشي ، هذا هو بارد جدا. أنا الوحيد الذي ذكرها؟ إليك استعلام رائع:

SELECT a.first_name, a.last_name, f.*
FROM actor AS a
LEFT OUTER JOIN LATERAL (
  SELECT f.title, SUM(amount) AS revenue
  FROM film AS f
  JOIN film_actor AS fa USING (film_id)
  JOIN inventory AS i USING (film_id)
  JOIN rental AS r USING (inventory_id)
  JOIN payment AS p USING (rental_id)
  WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
  GROUP BY f.film_id
  ORDER BY revenue DESC
  LIMIT 5
) AS f
ON true

سوف تجد أفضل 5 أفلام مدرة للدخل لكل ممثل. في كل مرة تحتاج فيها إلى استعلام TOP-N- لكل شيء ، سيكون LATERAL JOIN هو صديقك. إذا كنت شخصًا مزودًا لـ SQL Server ، فأنت تعرف هذا النوع JOIN تحت الاسم APPLY

SELECT a.first_name, a.last_name, f.*
FROM actor AS a
OUTER APPLY (
  SELECT f.title, SUM(amount) AS revenue
  FROM film AS f
  JOIN film_actor AS fa ON f.film_id = fa.film_id
  JOIN inventory AS i ON f.film_id = i.film_id
  JOIN rental AS r ON i.inventory_id = r.inventory_id
  JOIN payment AS p ON r.rental_id = p.rental_id
  WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
  GROUP BY f.film_id
  ORDER BY revenue DESC
  LIMIT 5
) AS f

حسنًا ، ربما يكون هذا غشًا ، لأن تعبير LATERAL JOIN أو APPLY هو في الحقيقة "استعلام فرعي مرتبط" ينتج عنه عدة صفوف. ولكن إذا سمحنا لـ "استعلامات فرعية مرتبطة" ، فيمكننا أيضًا التحدث عن ...

مولتيست

يتم تطبيق هذا فقط من قبل Oracle و Informix (على حد علمي) ، ولكن يمكن محاكاته في PostgreSQL باستخدام المصفوفات و/أو XML وفي SQL Server باستخدام XML.

MULTISET ينتج استعلام فرعي مرتبط ويعيد تداخل مجموعة الصفوف الناتجة في الاستعلام الخارجي. يحدد الاستعلام أدناه جميع الجهات الفاعلة ولكل ممثل يجمع أفلامه في مجموعة متداخلة:

SELECT a.*, MULTISET (
  SELECT f.*
  FROM film AS f
  JOIN film_actor AS fa USING (film_id)
  WHERE a.actor_id = fa.actor_id
) AS films
FROM actor

كما رأيت ، هناك أنواع أخرى من JOIN أكثر من "مملة" INNER و OUTER و CROSS JOIN المذكورة عادة. مزيد من التفاصيل في مقالتي . ورجاءً ، توقف عن استخدام مخططات Venn لتوضيحها.

36
Lukas Eder

لقد قمت بإنشاء توضيح يوضح أفضل من الكلمات ، في رأيي:  SQL Join table of explanation

9
Gisway