diff --git a/README.md b/README.md index fef4126..04fecf8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

-לוגו של מיזם לימוד הפייתון. נחש מצויר בצבעי צהוב וכחול, הנע בין האותיות של שם הקורס: לומדים פייתון. הסלוגן המופיע מעל לשם הקורס הוא מיזם חינמי ללימוד תכנות בעברית. +לוגו של מיזם לימוד הפייתון. נחש מצויר בצבעי צהוב וכחול, הנע בין האותיות של שם הקורס: לומדים פייתון. הסלוגן המופיע מעל לשם הקורס הוא מיזם חינמי ללימוד תכנות בעברית.

# קורס פייתון – מיזם חינמי ללימוד תכנות בעברית diff --git a/week1/1_Strings_and_Output.ipynb b/week01/1_Strings_and_Output.ipynb similarity index 100% rename from week1/1_Strings_and_Output.ipynb rename to week01/1_Strings_and_Output.ipynb diff --git a/week1/2_Arithmetics.ipynb b/week01/2_Arithmetics.ipynb similarity index 100% rename from week1/2_Arithmetics.ipynb rename to week01/2_Arithmetics.ipynb diff --git a/week1/3_Types.ipynb b/week01/3_Types.ipynb similarity index 100% rename from week1/3_Types.ipynb rename to week01/3_Types.ipynb diff --git a/week1/4_Variables.ipynb b/week01/4_Variables.ipynb similarity index 100% rename from week1/4_Variables.ipynb rename to week01/4_Variables.ipynb diff --git a/week1/5_Input_and_Casting.ipynb b/week01/5_Input_and_Casting.ipynb similarity index 100% rename from week1/5_Input_and_Casting.ipynb rename to week01/5_Input_and_Casting.ipynb diff --git a/week1/6_Booleans.ipynb b/week01/6_Booleans.ipynb similarity index 100% rename from week1/6_Booleans.ipynb rename to week01/6_Booleans.ipynb diff --git a/week1/7_Logic_Operators.ipynb b/week01/7_Logic_Operators.ipynb similarity index 100% rename from week1/7_Logic_Operators.ipynb rename to week01/7_Logic_Operators.ipynb diff --git a/week1/images/exercise.svg b/week01/images/exercise.svg similarity index 100% rename from week1/images/exercise.svg rename to week01/images/exercise.svg diff --git a/week1/images/laser_variables.svg b/week01/images/laser_variables.svg similarity index 100% rename from week1/images/laser_variables.svg rename to week01/images/laser_variables.svg diff --git a/week1/images/logo.jpg b/week01/images/logo.jpg similarity index 100% rename from week1/images/logo.jpg rename to week01/images/logo.jpg diff --git a/week1/images/pizza_cake.jpg b/week01/images/pizza_cake.jpg similarity index 100% rename from week1/images/pizza_cake.jpg rename to week01/images/pizza_cake.jpg diff --git a/week1/images/tip.png b/week01/images/tip.png similarity index 100% rename from week1/images/tip.png rename to week01/images/tip.png diff --git a/week1/images/true_and_true.svg b/week01/images/true_and_true.svg similarity index 100% rename from week1/images/true_and_true.svg rename to week01/images/true_and_true.svg diff --git a/week1/images/warning.png b/week01/images/warning.png similarity index 100% rename from week1/images/warning.png rename to week01/images/warning.png diff --git a/week2/1_Conditions.ipynb b/week02/1_Conditions.ipynb similarity index 100% rename from week2/1_Conditions.ipynb rename to week02/1_Conditions.ipynb diff --git a/week2/2_Conditions_part2.ipynb b/week02/2_Conditions_part2.ipynb similarity index 100% rename from week2/2_Conditions_part2.ipynb rename to week02/2_Conditions_part2.ipynb diff --git a/week2/3_Functions.ipynb b/week02/3_Functions.ipynb similarity index 99% rename from week2/3_Functions.ipynb rename to week02/3_Functions.ipynb index eb6f642..555d022 100644 --- a/week2/3_Functions.ipynb +++ b/week02/3_Functions.ipynb @@ -1210,7 +1210,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.9.0" } }, "nbformat": 4, diff --git a/week2/4_Lists.ipynb b/week02/4_Lists.ipynb similarity index 99% rename from week2/4_Lists.ipynb rename to week02/4_Lists.ipynb index 04ef3ca..a11642a 100644 --- a/week2/4_Lists.ipynb +++ b/week02/4_Lists.ipynb @@ -1444,7 +1444,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.9.0" } }, "nbformat": 4, diff --git a/week2/5_String_Methods.ipynb b/week02/5_String_Methods.ipynb similarity index 99% rename from week2/5_String_Methods.ipynb rename to week02/5_String_Methods.ipynb index 3bfa6c8..41fb0ee 100644 --- a/week2/5_String_Methods.ipynb +++ b/week02/5_String_Methods.ipynb @@ -1304,7 +1304,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.9.0" } }, "nbformat": 4, diff --git a/week2/6_Documentation.ipynb b/week02/6_Documentation.ipynb similarity index 100% rename from week2/6_Documentation.ipynb rename to week02/6_Documentation.ipynb diff --git a/week2/7_Summary.ipynb b/week02/7_Summary.ipynb similarity index 100% rename from week2/7_Summary.ipynb rename to week02/7_Summary.ipynb diff --git a/week2/images/COPYRIGHT.txt b/week02/images/COPYRIGHT.txt similarity index 100% rename from week2/images/COPYRIGHT.txt rename to week02/images/COPYRIGHT.txt diff --git a/week2/images/else-flow.svg b/week02/images/else-flow.svg similarity index 100% rename from week2/images/else-flow.svg rename to week02/images/else-flow.svg diff --git a/week2/images/exercise.svg b/week02/images/exercise.svg similarity index 100% rename from week2/images/exercise.svg rename to week02/images/exercise.svg diff --git a/week2/images/function-machine.png b/week02/images/function-machine.png similarity index 100% rename from week2/images/function-machine.png rename to week02/images/function-machine.png diff --git a/week2/images/if-flow.svg b/week02/images/if-flow.svg similarity index 100% rename from week2/images/if-flow.svg rename to week02/images/if-flow.svg diff --git a/week2/images/list-of-vinyls.png b/week02/images/list-of-vinyls.png similarity index 100% rename from week2/images/list-of-vinyls.png rename to week02/images/list-of-vinyls.png diff --git a/week2/images/logo.jpg b/week02/images/logo.jpg similarity index 100% rename from week2/images/logo.jpg rename to week02/images/logo.jpg diff --git a/week2/images/tip.png b/week02/images/tip.png similarity index 100% rename from week2/images/tip.png rename to week02/images/tip.png diff --git a/week2/images/warning.png b/week02/images/warning.png similarity index 100% rename from week2/images/warning.png rename to week02/images/warning.png diff --git a/week3/1_While_Loops.ipynb b/week03/1_While_Loops.ipynb similarity index 100% rename from week3/1_While_Loops.ipynb rename to week03/1_While_Loops.ipynb diff --git a/week3/2_Slicing.ipynb b/week03/2_Slicing.ipynb similarity index 100% rename from week3/2_Slicing.ipynb rename to week03/2_Slicing.ipynb diff --git a/week3/3_Files.ipynb b/week03/3_Files.ipynb similarity index 100% rename from week3/3_Files.ipynb rename to week03/3_Files.ipynb diff --git a/week3/4_List_Methods.ipynb b/week03/4_List_Methods.ipynb similarity index 100% rename from week3/4_List_Methods.ipynb rename to week03/4_List_Methods.ipynb diff --git a/week3/5_Mutability_and_Tuples.ipynb b/week03/5_Mutability_and_Tuples.ipynb similarity index 100% rename from week3/5_Mutability_and_Tuples.ipynb rename to week03/5_Mutability_and_Tuples.ipynb diff --git a/week3/Summary.ipynb b/week03/Summary.ipynb similarity index 100% rename from week3/Summary.ipynb rename to week03/Summary.ipynb diff --git a/week3/images/binary-png-representation.png b/week03/images/binary-png-representation.png similarity index 100% rename from week3/images/binary-png-representation.png rename to week03/images/binary-png-representation.png diff --git a/week3/images/exercise.svg b/week03/images/exercise.svg similarity index 100% rename from week3/images/exercise.svg rename to week03/images/exercise.svg diff --git a/week3/images/logo.jpg b/week03/images/logo.jpg similarity index 100% rename from week3/images/logo.jpg rename to week03/images/logo.jpg diff --git a/week3/images/textual-csv-representation.png b/week03/images/textual-csv-representation.png similarity index 100% rename from week3/images/textual-csv-representation.png rename to week03/images/textual-csv-representation.png diff --git a/week3/images/tip.png b/week03/images/tip.png similarity index 100% rename from week3/images/tip.png rename to week03/images/tip.png diff --git a/week3/images/warning.png b/week03/images/warning.png similarity index 100% rename from week3/images/warning.png rename to week03/images/warning.png diff --git a/week3/images/while-flow.svg b/week03/images/while-flow.svg similarity index 100% rename from week3/images/while-flow.svg rename to week03/images/while-flow.svg diff --git a/week3/images/while-song.svg b/week03/images/while-song.svg similarity index 100% rename from week3/images/while-song.svg rename to week03/images/while-song.svg diff --git a/week3/resources/cereal.csv b/week03/resources/cereal.csv similarity index 100% rename from week3/resources/cereal.csv rename to week03/resources/cereal.csv diff --git a/week3/resources/hope.txt b/week03/resources/hope.txt similarity index 100% rename from week3/resources/hope.txt rename to week03/resources/hope.txt diff --git a/week3/resources/passwords.txt b/week03/resources/passwords.txt similarity index 100% rename from week3/resources/passwords.txt rename to week03/resources/passwords.txt diff --git a/week4/1_For_Loops.ipynb b/week04/1_For_Loops.ipynb similarity index 100% rename from week4/1_For_Loops.ipynb rename to week04/1_For_Loops.ipynb diff --git a/week4/2_Dictionaries.ipynb b/week04/2_Dictionaries.ipynb similarity index 100% rename from week4/2_Dictionaries.ipynb rename to week04/2_Dictionaries.ipynb diff --git a/week4/3_Dictionaries_part2.ipynb b/week04/3_Dictionaries_part2.ipynb similarity index 100% rename from week4/3_Dictionaries_part2.ipynb rename to week04/3_Dictionaries_part2.ipynb diff --git a/week4/4_Unpacking.ipynb b/week04/4_Unpacking.ipynb similarity index 100% rename from week4/4_Unpacking.ipynb rename to week04/4_Unpacking.ipynb diff --git a/week4/5_Builtins.ipynb b/week04/5_Builtins.ipynb similarity index 100% rename from week4/5_Builtins.ipynb rename to week04/5_Builtins.ipynb diff --git a/week4/Summary.ipynb b/week04/Summary.ipynb similarity index 99% rename from week4/Summary.ipynb rename to week04/Summary.ipynb index c171e72..a3a6b24 100644 --- a/week4/Summary.ipynb +++ b/week04/Summary.ipynb @@ -389,7 +389,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.6" } }, "nbformat": 4, diff --git a/week4/images/deeper.svg b/week04/images/deeper.svg similarity index 100% rename from week4/images/deeper.svg rename to week04/images/deeper.svg diff --git a/week4/images/deeper2.svg b/week04/images/deeper2.svg similarity index 100% rename from week4/images/deeper2.svg rename to week04/images/deeper2.svg diff --git a/week4/images/dictionary.svg b/week04/images/dictionary.svg similarity index 100% rename from week4/images/dictionary.svg rename to week04/images/dictionary.svg diff --git a/week4/images/dictionary_groups.svg b/week04/images/dictionary_groups.svg similarity index 100% rename from week4/images/dictionary_groups.svg rename to week04/images/dictionary_groups.svg diff --git a/week04/images/even_dfa.svg b/week04/images/even_dfa.svg new file mode 100644 index 0000000..97c6fb3 --- /dev/null +++ b/week04/images/even_dfa.svg @@ -0,0 +1,3 @@ + + +
מספר זוגי
q1
מספר זוגי...
מספר אי־זוגי
q0
מספר אי־זוגי...
התחלה
התחלה
2, 4, 6, 8, 0
2, 4, 6, 8, 0
1, 3, 5, 7, 9
1, 3, 5, 7, 9
1, 3, 5, 7, 9
1, 3, 5, 7, 9
2, 4, 6, 8, 0
2, 4, 6, 8, 0
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week4/images/exercise.svg b/week04/images/exercise.svg similarity index 100% rename from week4/images/exercise.svg rename to week04/images/exercise.svg diff --git a/week4/images/logo.jpg b/week04/images/logo.jpg similarity index 100% rename from week4/images/logo.jpg rename to week04/images/logo.jpg diff --git a/week4/images/mutability1.svg b/week04/images/mutability1.svg similarity index 100% rename from week4/images/mutability1.svg rename to week04/images/mutability1.svg diff --git a/week4/images/mutability2.svg b/week04/images/mutability2.svg similarity index 100% rename from week4/images/mutability2.svg rename to week04/images/mutability2.svg diff --git a/week4/images/mutability3.svg b/week04/images/mutability3.svg similarity index 100% rename from week4/images/mutability3.svg rename to week04/images/mutability3.svg diff --git a/week4/images/recall.svg b/week04/images/recall.svg similarity index 100% rename from week4/images/recall.svg rename to week04/images/recall.svg diff --git a/week4/images/tip.png b/week04/images/tip.png similarity index 100% rename from week4/images/tip.png rename to week04/images/tip.png diff --git a/week4/images/unpacking.svg b/week04/images/unpacking.svg similarity index 100% rename from week4/images/unpacking.svg rename to week04/images/unpacking.svg diff --git a/week4/images/warning.png b/week04/images/warning.png similarity index 100% rename from week4/images/warning.png rename to week04/images/warning.png diff --git a/week4/resources/alice.txt b/week04/resources/alice.txt similarity index 100% rename from week4/resources/alice.txt rename to week04/resources/alice.txt diff --git a/week4/resources/elections.txt b/week04/resources/elections.txt similarity index 100% rename from week4/resources/elections.txt rename to week04/resources/elections.txt diff --git a/week4/resources/haiku.txt b/week04/resources/haiku.txt similarity index 100% rename from week4/resources/haiku.txt rename to week04/resources/haiku.txt diff --git a/week4/resources/war-and-peace.txt b/week04/resources/war-and-peace.txt similarity index 100% rename from week4/resources/war-and-peace.txt rename to week04/resources/war-and-peace.txt diff --git a/week5/1_Modules.ipynb b/week05/1_Modules.ipynb similarity index 100% rename from week5/1_Modules.ipynb rename to week05/1_Modules.ipynb diff --git a/week5/2_Functions_Part_2.ipynb b/week05/2_Functions_Part_2.ipynb similarity index 100% rename from week5/2_Functions_Part_2.ipynb rename to week05/2_Functions_Part_2.ipynb diff --git a/week5/3_Generators.ipynb b/week05/3_Generators.ipynb similarity index 100% rename from week5/3_Generators.ipynb rename to week05/3_Generators.ipynb diff --git a/week5/Summary.ipynb b/week05/Summary.ipynb similarity index 100% rename from week5/Summary.ipynb rename to week05/Summary.ipynb diff --git a/week5/images/deeper.svg b/week05/images/deeper.svg similarity index 100% rename from week5/images/deeper.svg rename to week05/images/deeper.svg diff --git a/week5/images/deeper2.svg b/week05/images/deeper2.svg similarity index 100% rename from week5/images/deeper2.svg rename to week05/images/deeper2.svg diff --git a/week5/images/exercise.svg b/week05/images/exercise.svg similarity index 100% rename from week5/images/exercise.svg rename to week05/images/exercise.svg diff --git a/week5/images/logo.jpg b/week05/images/logo.jpg similarity index 100% rename from week5/images/logo.jpg rename to week05/images/logo.jpg diff --git a/week5/images/recall.svg b/week05/images/recall.svg similarity index 100% rename from week5/images/recall.svg rename to week05/images/recall.svg diff --git a/week5/images/silly_generator1.png b/week05/images/silly_generator1.png similarity index 100% rename from week5/images/silly_generator1.png rename to week05/images/silly_generator1.png diff --git a/week5/images/tip.png b/week05/images/tip.png similarity index 100% rename from week5/images/tip.png rename to week05/images/tip.png diff --git a/week5/images/warning.png b/week05/images/warning.png similarity index 100% rename from week5/images/warning.png rename to week05/images/warning.png diff --git a/week5/resources/logo.jpg b/week05/resources/logo.jpg similarity index 100% rename from week5/resources/logo.jpg rename to week05/resources/logo.jpg diff --git a/week5/resources/potter.zip b/week05/resources/potter.zip similarity index 100% rename from week5/resources/potter.zip rename to week05/resources/potter.zip diff --git a/week5/resources/pride-and-prejudice.txt b/week05/resources/pride-and-prejudice.txt similarity index 100% rename from week5/resources/pride-and-prejudice.txt rename to week05/resources/pride-and-prejudice.txt diff --git a/week5/resources/war-and-peace.txt b/week05/resources/war-and-peace.txt similarity index 100% rename from week5/resources/war-and-peace.txt rename to week05/resources/war-and-peace.txt diff --git a/week6/1_Sets.ipynb b/week06/1_Sets.ipynb similarity index 99% rename from week6/1_Sets.ipynb rename to week06/1_Sets.ipynb index 7cfd0ba..a7772da 100644 --- a/week6/1_Sets.ipynb +++ b/week06/1_Sets.ipynb @@ -861,7 +861,7 @@ " return path1_files & path2_files\n", "\n", "\n", - "common_filenames('images', 'resources/week5_images')" + "common_filenames('images', 'resources/week05_images')" ] }, { diff --git a/week6/2_Functional_Behavior.ipynb b/week06/2_Functional_Behavior.ipynb similarity index 100% rename from week6/2_Functional_Behavior.ipynb rename to week06/2_Functional_Behavior.ipynb diff --git a/week6/3_Comprehensions.ipynb b/week06/3_Comprehensions.ipynb similarity index 100% rename from week6/3_Comprehensions.ipynb rename to week06/3_Comprehensions.ipynb diff --git a/week6/4_Modules_Part_2.ipynb b/week06/4_Modules_Part_2.ipynb similarity index 100% rename from week6/4_Modules_Part_2.ipynb rename to week06/4_Modules_Part_2.ipynb diff --git a/week06/5_Summary.ipynb b/week06/5_Summary.ipynb new file mode 100644 index 0000000..34b2138 --- /dev/null +++ b/week06/5_Summary.ipynb @@ -0,0 +1,192 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# תרגילים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### group_by" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו פונקציה בשם group_by שמקבלת פונקציה כפרמטר ראשון, ו־iterable כפרמטר שני.
\n", + " הפונקציה תחזיר מילון, שבו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + "לדוגמה, עבור הקריאה group_by(len, [\"hi\", \"bye\", \"yo\", \"try\"]) יוחזר הערך: {2: [\"hi\", \"yo\"], 3: [\"bye\", \"try\"]}.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### zipwith" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו פונקציה בשם zip_with שמקבלת פונקציה כפרמטר ראשון, ושני iterable־ים או יותר בפרמטרים שאחריו.
\n", + " הפונקציה תחזיר רשימה, שבה האיבר במקום ה־N־י הוא הערך שחזר מהעברת כל הערכים במקום ה־N־י של כל ה־iterables לפונקציה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + "לדוגמה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אפשר להניח שה־iterables המועברים לפונקציה זהים באורכם.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### זכרתם?\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו פונקציה שמקבלת מסר להצפנה, ויוצרת ממנו תמונה מוצפנת.
\n", + " השתמשו בשיטת ההצפנה שהוצגה במחברת הקודמת. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### סט" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למדו את החוקים של המשחק סט, מהערך בוויקיפדיה או מ־YouTube.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "
  1. צרו חפיסת סט.
  2. \n", + "
  3. טרפו אותה היטב, ופתחו 12 קלפים על השולחן. הדפיסו את כל הסטים שמצאתם.
  4. \n", + "
  5. בדקו בכמה אחוזים מהפעמים שבהן פותחים 12 קלפים אקראיים מהחפיסה – אין אף סט על הלוח.
  6. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי לחשב את סעיף 3, הריצו את הבדיקה על 10,000 מקרים שבהם פתחתם 12 קלפים מהחפיסה המעורבבת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 100 מעלות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו קוד שמוצא את 100 השירים הפופולריים ביותר לפי מדד Hot 100 של Billboard.
\n", + " השיגו את המילים של השירים שמצאתם, ושרטטו גרף שמראה כמה פעמים מופיעה כל מילה מ־100 המילים הנפוצות ביותר בכל השירים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בונוס: בצעו ניתוח מעניין אחר, כמו מיהם האומנים שמשתמשים בהכי הרבה מילים בשירים שלהם!\n", + "

" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/week6/images/deeper.svg b/week06/images/deeper.svg similarity index 100% rename from week6/images/deeper.svg rename to week06/images/deeper.svg diff --git a/week6/images/deeper2.svg b/week06/images/deeper2.svg similarity index 100% rename from week6/images/deeper2.svg rename to week06/images/deeper2.svg diff --git a/week6/images/exercise.svg b/week06/images/exercise.svg similarity index 100% rename from week6/images/exercise.svg rename to week06/images/exercise.svg diff --git a/week6/images/for_vs_listcomp.png b/week06/images/for_vs_listcomp.png similarity index 100% rename from week6/images/for_vs_listcomp.png rename to week06/images/for_vs_listcomp.png diff --git a/week6/images/for_vs_listcomp.svg b/week06/images/for_vs_listcomp.svg similarity index 100% rename from week6/images/for_vs_listcomp.svg rename to week06/images/for_vs_listcomp.svg diff --git a/week6/images/for_vs_listcomp_with_if.png b/week06/images/for_vs_listcomp_with_if.png similarity index 100% rename from week6/images/for_vs_listcomp_with_if.png rename to week06/images/for_vs_listcomp_with_if.png diff --git a/week6/images/for_vs_listcomp_with_if.svg b/week06/images/for_vs_listcomp_with_if.svg similarity index 100% rename from week6/images/for_vs_listcomp_with_if.svg rename to week06/images/for_vs_listcomp_with_if.svg diff --git a/week6/images/generator_vs_expression.png b/week06/images/generator_vs_expression.png similarity index 100% rename from week6/images/generator_vs_expression.png rename to week06/images/generator_vs_expression.png diff --git a/week6/images/generator_vs_expression.svg b/week06/images/generator_vs_expression.svg similarity index 100% rename from week6/images/generator_vs_expression.svg rename to week06/images/generator_vs_expression.svg diff --git a/week6/images/google_module_search.png b/week06/images/google_module_search.png similarity index 100% rename from week6/images/google_module_search.png rename to week06/images/google_module_search.png diff --git a/week6/images/lambda.png b/week06/images/lambda.png similarity index 100% rename from week6/images/lambda.png rename to week06/images/lambda.png diff --git a/week6/images/lambda.svg b/week06/images/lambda.svg similarity index 100% rename from week6/images/lambda.svg rename to week06/images/lambda.svg diff --git a/week6/images/logo.jpg b/week06/images/logo.jpg similarity index 100% rename from week6/images/logo.jpg rename to week06/images/logo.jpg diff --git a/week6/images/pypi_wikipedia.png b/week06/images/pypi_wikipedia.png similarity index 100% rename from week6/images/pypi_wikipedia.png rename to week06/images/pypi_wikipedia.png diff --git a/week6/images/recall.svg b/week06/images/recall.svg similarity index 100% rename from week6/images/recall.svg rename to week06/images/recall.svg diff --git a/week6/images/tip.png b/week06/images/tip.png similarity index 100% rename from week6/images/tip.png rename to week06/images/tip.png diff --git a/week6/images/venn.svg b/week06/images/venn.svg similarity index 100% rename from week6/images/venn.svg rename to week06/images/venn.svg diff --git a/week6/images/venn2.svg b/week06/images/venn2.svg similarity index 100% rename from week6/images/venn2.svg rename to week06/images/venn2.svg diff --git a/week6/images/warning.png b/week06/images/warning.png similarity index 100% rename from week6/images/warning.png rename to week06/images/warning.png diff --git a/week6/resources/code.png b/week06/resources/code.png similarity index 100% rename from week6/resources/code.png rename to week06/resources/code.png diff --git a/week6/resources/hamlet.txt b/week06/resources/hamlet.txt similarity index 100% rename from week6/resources/hamlet.txt rename to week06/resources/hamlet.txt diff --git a/week6/resources/pride-and-prejudice.txt b/week06/resources/pride-and-prejudice.txt similarity index 100% rename from week6/resources/pride-and-prejudice.txt rename to week06/resources/pride-and-prejudice.txt diff --git a/week6/resources/states.txt b/week06/resources/states.txt similarity index 100% rename from week6/resources/states.txt rename to week06/resources/states.txt diff --git a/week6/resources/the-monkeys-paw.txt b/week06/resources/the-monkeys-paw.txt similarity index 100% rename from week6/resources/the-monkeys-paw.txt rename to week06/resources/the-monkeys-paw.txt diff --git a/week6/resources/week5_images/deeper.svg b/week06/resources/week5_images/deeper.svg similarity index 100% rename from week6/resources/week5_images/deeper.svg rename to week06/resources/week5_images/deeper.svg diff --git a/week6/resources/week5_images/deeper2.svg b/week06/resources/week5_images/deeper2.svg similarity index 100% rename from week6/resources/week5_images/deeper2.svg rename to week06/resources/week5_images/deeper2.svg diff --git a/week6/resources/week5_images/exercise.svg b/week06/resources/week5_images/exercise.svg similarity index 100% rename from week6/resources/week5_images/exercise.svg rename to week06/resources/week5_images/exercise.svg diff --git a/week6/resources/week5_images/logo.jpg b/week06/resources/week5_images/logo.jpg similarity index 100% rename from week6/resources/week5_images/logo.jpg rename to week06/resources/week5_images/logo.jpg diff --git a/week6/resources/week5_images/recall.svg b/week06/resources/week5_images/recall.svg similarity index 100% rename from week6/resources/week5_images/recall.svg rename to week06/resources/week5_images/recall.svg diff --git a/week6/resources/week5_images/silly_generator1.png b/week06/resources/week5_images/silly_generator1.png similarity index 100% rename from week6/resources/week5_images/silly_generator1.png rename to week06/resources/week5_images/silly_generator1.png diff --git a/week6/resources/week5_images/tip.png b/week06/resources/week5_images/tip.png similarity index 100% rename from week6/resources/week5_images/tip.png rename to week06/resources/week5_images/tip.png diff --git a/week6/resources/week5_images/warning.png b/week06/resources/week5_images/warning.png similarity index 100% rename from week6/resources/week5_images/warning.png rename to week06/resources/week5_images/warning.png diff --git a/week6/resources/words.txt b/week06/resources/words.txt similarity index 100% rename from week6/resources/words.txt rename to week06/resources/words.txt diff --git a/week7/1_Classes.ipynb b/week07/1_Classes.ipynb similarity index 99% rename from week7/1_Classes.ipynb rename to week07/1_Classes.ipynb index 8b23d3c..78119c7 100644 --- a/week7/1_Classes.ipynb +++ b/week07/1_Classes.ipynb @@ -2046,7 +2046,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.6" } }, "nbformat": 4, diff --git a/week7/2_Documentation.ipynb b/week07/2_Documentation.ipynb similarity index 100% rename from week7/2_Documentation.ipynb rename to week07/2_Documentation.ipynb diff --git a/week7/3_Classes_Part_2.ipynb b/week07/3_Classes_Part_2.ipynb similarity index 100% rename from week7/3_Classes_Part_2.ipynb rename to week07/3_Classes_Part_2.ipynb diff --git a/week7/4_Summary.ipynb b/week07/4_Summary.ipynb similarity index 100% rename from week7/4_Summary.ipynb rename to week07/4_Summary.ipynb diff --git a/week7/images/deeper.svg b/week07/images/deeper.svg similarity index 100% rename from week7/images/deeper.svg rename to week07/images/deeper.svg diff --git a/week7/images/deeper2.svg b/week07/images/deeper2.svg similarity index 100% rename from week7/images/deeper2.svg rename to week07/images/deeper2.svg diff --git a/week7/images/exercise.svg b/week07/images/exercise.svg similarity index 100% rename from week7/images/exercise.svg rename to week07/images/exercise.svg diff --git a/week7/images/lego_brick.svg b/week07/images/lego_brick.svg similarity index 100% rename from week7/images/lego_brick.svg rename to week07/images/lego_brick.svg diff --git a/week7/images/logo.jpg b/week07/images/logo.jpg similarity index 100% rename from week7/images/logo.jpg rename to week07/images/logo.jpg diff --git a/week7/images/recall.svg b/week07/images/recall.svg similarity index 100% rename from week7/images/recall.svg rename to week07/images/recall.svg diff --git a/week7/images/tip.png b/week07/images/tip.png similarity index 100% rename from week7/images/tip.png rename to week07/images/tip.png diff --git a/week7/images/user_class.svg b/week07/images/user_class.svg similarity index 100% rename from week7/images/user_class.svg rename to week07/images/user_class.svg diff --git a/week7/images/warning.png b/week07/images/warning.png similarity index 100% rename from week7/images/warning.png rename to week07/images/warning.png diff --git a/week8/1_Inheritance.ipynb b/week08/1_Inheritance.ipynb similarity index 99% rename from week8/1_Inheritance.ipynb rename to week08/1_Inheritance.ipynb index f3b8b19..06b3a40 100644 --- a/week8/1_Inheritance.ipynb +++ b/week08/1_Inheritance.ipynb @@ -2316,7 +2316,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "week8\n", + "week08\n", "│ 1_Inheritance.ipynb\n", "│ 2_Inheritance_Part_2.ipynb\n", "│ 3_Exceptions.ipynb\n", @@ -2334,9 +2334,9 @@ "metadata": {}, "source": [ "

\n", - " בדוגמה שלמעלה יש 5 קבצים תחת תיקיית week8: שניים מהם תיקיות (images, resources) ו־3 מהם מחברות.
\n", + " בדוגמה שלמעלה יש 5 קבצים תחת תיקיית week08: שניים מהם תיקיות (images, resources) ו־3 מהם מחברות.
\n", " התיקייה resources ריקה, ובתיקייה images יש את הקבצים הטקסטואליים exercise.svg ו־recall.svg ואת הקובץ הבינארי logo.jpg.
\n", - " במערכת שלנו, הנתיב לתיקיית week8 הוא /week8, והנתיב לקובץ logo.jpg הוא /week8/images/logo.jpg.\n", + " במערכת שלנו, הנתיב לתיקיית week08 הוא /week08, והנתיב לקובץ logo.jpg הוא /week08/images/logo.jpg.\n", "

" ] }, @@ -2458,7 +2458,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.6" } }, "nbformat": 4, diff --git a/week8/2_Inheritance_Part_2.ipynb b/week08/2_Inheritance_Part_2.ipynb similarity index 99% rename from week8/2_Inheritance_Part_2.ipynb rename to week08/2_Inheritance_Part_2.ipynb index 92ef5a1..6b1237e 100644 --- a/week8/2_Inheritance_Part_2.ipynb +++ b/week08/2_Inheritance_Part_2.ipynb @@ -1762,7 +1762,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.9.0" } }, "nbformat": 4, diff --git a/week8/3_Exceptions.ipynb b/week08/3_Exceptions.ipynb similarity index 98% rename from week8/3_Exceptions.ipynb rename to week08/3_Exceptions.ipynb index 7afcc53..da07fac 100644 --- a/week8/3_Exceptions.ipynb +++ b/week08/3_Exceptions.ipynb @@ -1575,7 +1575,7 @@ "metadata": {}, "outputs": [], "source": [ - "search_in_directory(r\"C:\\Projects\\Notebooks\\week8\", [\"class\", \"int\"])" + "search_in_directory(r\"C:\\Projects\\Notebooks\\week08\", [\"class\", \"int\"])" ] }, { @@ -1594,29 +1594,29 @@ "----------------------------------------\n", "class\n", "----------------------------------------\n", - "C:\\Projects\\Notebooks\\week8\\1_Inheritance.ipynb\n", - "C:\\Projects\\Notebooks\\week8\\2_Inheritance_Part_2.ipynb\n", - "C:\\Projects\\Notebooks\\week8\\3_Exceptions.ipynb\n", - "C:\\Projects\\Notebooks\\week8\\4_Exceptions_Part_2.ipynb\n", - "C:\\Projects\\Notebooks\\week8\\images\\exception_parts.svg\n", + "C:\\Projects\\Notebooks\\week08\\1_Inheritance.ipynb\n", + "C:\\Projects\\Notebooks\\week08\\2_Inheritance_Part_2.ipynb\n", + "C:\\Projects\\Notebooks\\week08\\3_Exceptions.ipynb\n", + "C:\\Projects\\Notebooks\\week08\\4_Exceptions_Part_2.ipynb\n", + "C:\\Projects\\Notebooks\\week08\\images\\exception_parts.svg\n", "----------------------------------------\n", "int\n", "----------------------------------------\n", - "C:\\Projects\\Notebooks\\week8\\1_Inheritance.ipynb\n", - "C:\\Projects\\Notebooks\\week8\\2_Inheritance_Part_2.ipynb\n", - "C:\\Projects\\Notebooks\\week8\\3_Exceptions.ipynb\n", - "C:\\Projects\\Notebooks\\week8\\4_Exceptions_Part_2.ipynb\n", - "C:\\Projects\\Notebooks\\week8\\images\\chessboard.svg\n", - "C:\\Projects\\Notebooks\\week8\\images\\diamond_problem.svg\n", - "C:\\Projects\\Notebooks\\week8\\images\\exception_parts.svg\n", - "C:\\Projects\\Notebooks\\week8\\images\\exception_propogation.svg\n", - "C:\\Projects\\Notebooks\\week8\\images\\inheritance.svg\n", - "C:\\Projects\\Notebooks\\week8\\images\\logo.jpg\n", - "C:\\Projects\\Notebooks\\week8\\images\\multilevel_inheritance.svg\n", - "C:\\Projects\\Notebooks\\week8\\images\\multiple_inheritance.svg\n", - "C:\\Projects\\Notebooks\\week8\\images\\multiple_inheritance.svg.old\n", - "C:\\Projects\\Notebooks\\week8\\images\\try_except_flow.svg\n", - "C:\\Projects\\Notebooks\\week8\\images\\try_except_syntax.svg" + "C:\\Projects\\Notebooks\\week08\\1_Inheritance.ipynb\n", + "C:\\Projects\\Notebooks\\week08\\2_Inheritance_Part_2.ipynb\n", + "C:\\Projects\\Notebooks\\week08\\3_Exceptions.ipynb\n", + "C:\\Projects\\Notebooks\\week08\\4_Exceptions_Part_2.ipynb\n", + "C:\\Projects\\Notebooks\\week08\\images\\chessboard.svg\n", + "C:\\Projects\\Notebooks\\week08\\images\\diamond_problem.svg\n", + "C:\\Projects\\Notebooks\\week08\\images\\exception_parts.svg\n", + "C:\\Projects\\Notebooks\\week08\\images\\exception_propogation.svg\n", + "C:\\Projects\\Notebooks\\week08\\images\\inheritance.svg\n", + "C:\\Projects\\Notebooks\\week08\\images\\logo.jpg\n", + "C:\\Projects\\Notebooks\\week08\\images\\multilevel_inheritance.svg\n", + "C:\\Projects\\Notebooks\\week08\\images\\multiple_inheritance.svg\n", + "C:\\Projects\\Notebooks\\week08\\images\\multiple_inheritance.svg.old\n", + "C:\\Projects\\Notebooks\\week08\\images\\try_except_flow.svg\n", + "C:\\Projects\\Notebooks\\week08\\images\\try_except_syntax.svg" ] }, { @@ -1645,7 +1645,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.6" } }, "nbformat": 4, diff --git a/week8/4_Exceptions_Part_2.ipynb b/week08/4_Exceptions_Part_2.ipynb similarity index 99% rename from week8/4_Exceptions_Part_2.ipynb rename to week08/4_Exceptions_Part_2.ipynb index 33553c2..dd65467 100644 --- a/week8/4_Exceptions_Part_2.ipynb +++ b/week08/4_Exceptions_Part_2.ipynb @@ -1685,7 +1685,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.6" } }, "nbformat": 4, diff --git a/week8/5_Summary.ipynb b/week08/5_Summary.ipynb similarity index 99% rename from week8/5_Summary.ipynb rename to week08/5_Summary.ipynb index c67a98c..471c288 100644 --- a/week8/5_Summary.ipynb +++ b/week08/5_Summary.ipynb @@ -282,7 +282,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.6" } }, "nbformat": 4, diff --git a/week8/images/chessboard.svg b/week08/images/chessboard.svg similarity index 100% rename from week8/images/chessboard.svg rename to week08/images/chessboard.svg diff --git a/week8/images/circuit_example.svg b/week08/images/circuit_example.svg similarity index 100% rename from week8/images/circuit_example.svg rename to week08/images/circuit_example.svg diff --git a/week8/images/complex_circuit.svg b/week08/images/complex_circuit.svg similarity index 100% rename from week8/images/complex_circuit.svg rename to week08/images/complex_circuit.svg diff --git a/week8/images/deeper.svg b/week08/images/deeper.svg similarity index 100% rename from week8/images/deeper.svg rename to week08/images/deeper.svg diff --git a/week8/images/deeper2.svg b/week08/images/deeper2.svg similarity index 100% rename from week8/images/deeper2.svg rename to week08/images/deeper2.svg diff --git a/week8/images/diamond_problem.svg b/week08/images/diamond_problem.svg similarity index 100% rename from week8/images/diamond_problem.svg rename to week08/images/diamond_problem.svg diff --git a/week8/images/exception_parts.svg b/week08/images/exception_parts.svg similarity index 100% rename from week8/images/exception_parts.svg rename to week08/images/exception_parts.svg diff --git a/week8/images/exception_propogation.svg b/week08/images/exception_propogation.svg similarity index 100% rename from week8/images/exception_propogation.svg rename to week08/images/exception_propogation.svg diff --git a/week8/images/exercise.svg b/week08/images/exercise.svg similarity index 100% rename from week8/images/exercise.svg rename to week08/images/exercise.svg diff --git a/week8/images/inheritance.svg b/week08/images/inheritance.svg similarity index 100% rename from week8/images/inheritance.svg rename to week08/images/inheritance.svg diff --git a/week8/images/logo.jpg b/week08/images/logo.jpg similarity index 100% rename from week8/images/logo.jpg rename to week08/images/logo.jpg diff --git a/week8/images/multilevel_inheritance.svg b/week08/images/multilevel_inheritance.svg similarity index 100% rename from week8/images/multilevel_inheritance.svg rename to week08/images/multilevel_inheritance.svg diff --git a/week8/images/multiple_inheritance.svg b/week08/images/multiple_inheritance.svg similarity index 100% rename from week8/images/multiple_inheritance.svg rename to week08/images/multiple_inheritance.svg diff --git a/week8/images/multiple_inheritance.svg.old b/week08/images/multiple_inheritance.svg.old similarity index 100% rename from week8/images/multiple_inheritance.svg.old rename to week08/images/multiple_inheritance.svg.old diff --git a/week8/images/nightrider.png b/week08/images/nightrider.png similarity index 100% rename from week8/images/nightrider.png rename to week08/images/nightrider.png diff --git a/week8/images/recall.svg b/week08/images/recall.svg similarity index 100% rename from week8/images/recall.svg rename to week08/images/recall.svg diff --git a/week8/images/tip.png b/week08/images/tip.png similarity index 100% rename from week8/images/tip.png rename to week08/images/tip.png diff --git a/week8/images/try_except_flow.svg b/week08/images/try_except_flow.svg similarity index 100% rename from week8/images/try_except_flow.svg rename to week08/images/try_except_flow.svg diff --git a/week8/images/try_except_flow_full.svg b/week08/images/try_except_flow_full.svg similarity index 100% rename from week8/images/try_except_flow_full.svg rename to week08/images/try_except_flow_full.svg diff --git a/week8/images/try_except_syntax.svg b/week08/images/try_except_syntax.svg similarity index 100% rename from week8/images/try_except_syntax.svg rename to week08/images/try_except_syntax.svg diff --git a/week8/images/warning.png b/week08/images/warning.png similarity index 100% rename from week8/images/warning.png rename to week08/images/warning.png diff --git a/week8/images/wildebeest.png b/week08/images/wildebeest.png similarity index 100% rename from week8/images/wildebeest.png rename to week08/images/wildebeest.png diff --git a/week8/resources/castle.txt b/week08/resources/castle.txt similarity index 100% rename from week8/resources/castle.txt rename to week08/resources/castle.txt diff --git a/week8/resources/message.txt b/week08/resources/message.txt similarity index 100% rename from week8/resources/message.txt rename to week08/resources/message.txt diff --git a/week8/resources/passwords.txt b/week08/resources/passwords.txt similarity index 100% rename from week8/resources/passwords.txt rename to week08/resources/passwords.txt diff --git a/week8/resources/users.txt b/week08/resources/users.txt similarity index 100% rename from week8/resources/users.txt rename to week08/resources/users.txt diff --git a/week13/1_Data.ipynb b/week13/1_Data.ipynb new file mode 100644 index 0000000..2d138bf --- /dev/null +++ b/week13/1_Data.ipynb @@ -0,0 +1,1946 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"לוגו" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# עבודה עם מידע" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## הקדמה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אחת מהמהפיכות המשמעותיות שאנחנו חווים בעידן המודרני היא התפוצצות חסרת תקדים של כמות המידע שנמצא בהישג ידינו.
\n", + " בזכות המחשוב והכלים המתקדמים שפיתחנו, איסוף נתונים לגבי כמעט כל דבר זה דבר שבשגרה.
\n", + " המוסכמות לגבי הצורך בשקיפות עבור אותם נתונים תופסים תאוצה, והשימוש בהם והניתוח שלהם עוזר לנו להגיע למסקנות מהר יותר מאי פעם.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " גופים רבים מתרגלים לחשוף את הנתונים שלהם לציבור: החל בחברות הגדולות, דרך ארגוני שקיפות וכלה בארגונים ממשלתיים.
\n", + " אבל מה הם בכלל אותם נתונים, ואת מי הם משמשים?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ניתן דוגמאות לכמה מאגרי נתונים מעניינים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + "נסו לדמיין כמה מסקנות מעניינות מתכנת מנוסה יכול להפיק מבסיסי הנתונים הללו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " וזה לא מסתיים כאן – ההתעסקות בנתונים הפכה להיות ממש ענף שלם!
\n", + " בעידן המודרני קשה להתחמק מהמושג \"Big Data\" (נְתוּנֵי עָתֵק), שמדבר על מאגרי מידע בלתי נתפסים בגודלם.
\n", + " אמנם מדעי הנתונים הוא תחום עיסוק עצום שנספיק לגעת רק בקצה־קצהו במסגרת הקורס, אבל הוא בהחלט עשיר ומרתק.
\n", + " הנה כמה דוגמאות מעניינות לשימושים שלו בעולם כיום:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במסגרת השבוע אמנם לא נעסוק בנתוני עתק, אבל בהחלט נעשה את צעדינו הראשונים בעבודה עם מאגרי מידע.
\n", + " נלמד לקרוא מידע, לאחסן אותו, ליצור מאגרי מידע בעצמנו ולהפיק ממידע שכזה תועלת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל פתיחה: הבר באורנוס" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + "חברנו החביב הרשל מנהל בר בכוכב אורנוס המרוחק.
\n", + " לבר לקוחות רבים, אך לאחרונה האיגוד ההיפר־גלקטי מקשה עליו בניהול המקום עם תקנות משונות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " משהתרגל למצב, הרשל מנסה להגדיל את רווחיו מהמכירות בבר.
\n", + " הוא מתכוון לשפר את החשיפה לקהל חדש, ובד בבד גם להדק את התפריט שמוגש ללקוחות.
\n", + " כמו שאתם ודאי מתארים לעצמכם, להרשל יש מערכת המאגדת את הזמנותיהם של כל לקוחותיו.
\n", + " המידע מסודר בצורה טבלאית המכילה את זמן ההזמנה מדויק, את המוצר שהוזמן ומחירו במערכת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " השתמשו בקובץ resources/cocktails.csv כדי לענות על השאלות הבאות:
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ארגון נתונים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אחרי שריעננתם קצת את הזיכרון שלכם בפייתון ועבדתם קצת עם נתונים,
\n", + " ודאי חשתם בסרבול שבהתעסקות עם הנתונים – במיוחד אם לא השתמשתם במודולים חיצוניים.
\n", + " השגיאות במהלך ניסיון הטיפול בקובץ, הסרבול שבהפרדה לשדות והצורך לכתוב קוד ארוך –
\n", + " כולם הופכים את ההתעסקות עם מידע מהסוג הזה לפחות כייפי.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עוד לא הכנסנו לתמונה את שאר הבעיות שבעבודה עם קבצים כאלו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למזלנו, בשנות ה־80 נוצרה טכנולוגיה פופולרית שמטרתה לפתור את הבעיות הללו – ועוד בעיות רבות נוספות.
\n", + " לטכנולוגיה קוראים מסדי נתונים רלציוניים (Relational databases),
\n", + " והיא טכנולוגיה פופולרית מאוד שעוזרת לנו לאחסן נתונים בצורה טבלאית, לנהל אותם ולשאול עליהם שאלות.
\n", + " כמה היה נוח לו הייתה להרשל טכנולוגיה שכזו, הא?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### איך נראה מסד נתונים?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במקרה של הרשל, היינו כנראה מאחסנים את הנתונים תחת טבלה שנקראת drinks_sold.
\n", + " כל שורה הייתה מייצגת ישות – אוסף סדור אחד של פריטים, רכישה אחת של משקה.
\n", + " במקרה שלנו אוסף סדור יחיד מכיל את: מספר ההזמנה, שם המזמין, תאריך ההזמנה, שם המשקה ומחירו. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשבוע הקרוב אנחנו הולכים להתעסק עם טבלאות שמכילות נתונים שכאלו.
\n", + " מסד נתונים יורכב מטבלה אחת או יותר, ובכל טבלה שכזו יהיו לנו שורות רבות.
\n", + " כל שורה מייצגת ישות אחת (פריט אחד) באוסף הנתונים שלנו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בהקבלה לפייתון, אפשר לדמיין שורה בטבלה כ־tuple:
\n", + " רצף של כמה נתונים שבאים אחד אחרי השני, שלכל אחד מהם יש טיפוס מסוים וערך מסוים.
\n", + " אם נרצה לדייק אפילו יותר, נוכל לייצג שורה בטבלה כמילון:
\n", + " עבור כל עמודה בטבלה יהיה מפתח תואם במילון, ואליו ייכנס הערך שנמצא באותה עמודה. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בתמונה\n", + "
\n", + " דוגמה לאחסון נתונים בצורה טבלאית.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אחסון השורה הראשונה כ־tuple יראה כך:\n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [], + "source": [ + "row1 = (1, \"Ronit Feldman\", datetime.datetime(2019, 11, 9), \"Crazy Earl\", 57)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ומילון שנוצר מהשורה הראשונה יראה כך:\n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [], + "source": [ + "row1 = {\n", + " \"id\": 1,\n", + " \"name\": \"Ronit Feldman\",\n", + " \"order_date\": datetime.datetime(2019, 11, 9),\n", + " \"drink_name\": \"Crazy Earl\",\n", + " \"drink_price\": 57,\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " סביר שעד כה דמיינתם את הנתונים כטבלה ב־Excel (או Google Sheets, אנחנו לא שופטים).
\n", + " נראה שצדקתם! זה באמת דומה מאוד.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם כן, אוסף של שורות כאלו הוא טבלה, וכמה טבלאות יחדיו יוצרות לנו מסד נתונים של ממש.
\n", + " נוכל לדמיין טבלה כרשימה של tuple־ים, או כרשימה של מילונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " יצגו את טבלת drinks_sold הירוקה שהופיעה למעלה בעזרת מבנה נתונים פייתוני.
\n", + " השתמשו במילונים לשם כך, ושמרו את מבנה הנתונים שמאגד אותם במשתנה ששמו drinks_sold.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## שאילתה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### המנוע" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הרכיב המעניין ביותר בכל מסד נתונים הוא \"המנוע\", או מערכת הניהול של מסד הנתונים (DBMS).
\n", + " זו התוכנה שעובדת מאחורי הקלעים, ומאפשרת לכם לערוך את המידע במסד הנתונים ולשאול שאלות לגביו.
\n", + " חברות גדולות משקיעות משאבים רבים כדי לייעל ולשפר את הביצועים של המנועים הללו.
\n", + " לבינתיים, אנחנו נשתמש במנוע קליל שנקרא SQLite.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אחזור מידע ממסדי הנתונים מתבצע באמצעות ניסוח שאלות בשפה שנקראת SQL.
\n", + " צעדינו הראשונים יעסקו בשימוש בשפה לצורך תחקור הנתונים הקיימים במסד הנתונים.
\n", + " בעתיד, נרחיב את יכולותינו כך שנוכל גם לעדכן, להוסיף או למחוק את הנתונים במסד.
\n", + " פקודת אחזור או שינוי של מידע במסד הנתונים שנכתבה ב־SQL נקראת \"שאילתה\" (או query).
\n", + " במהלך יחידת הלימוד הזו נלמד לנסח שאילתות כדי לאחזר או לשנות מידע, ונריץ אותן בעזרת המנוע של מסד הנתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " השאילתות שנלמד לבצע יחזירו מידע, גם הן, בצורה טבלאית.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בתמונה\n", + "
\n", + " דוגמה לתוצאת שאילתה שהרצנו על מסד הנתונים של פורום הקורס בשביל הדגמה.
\n", + " בשאילתה ביקשנו לאחזר את מספר המשתמש, השם, הכינוי, מצב הניהול ותאריך יום ההולדת של כל אחד מהמשתמשים בפורום.
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### הממשק" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לצורך השבוע, הכנו לכם מסד נתונים פשוט עליו תוכלו להתנסות.
\n", + " בנינו לכם גם אתר שיאפשר לכם להעלות מסד נתונים ולהריץ עליו שאילתות, בכתובת sql.pythonic.guru.
\n", + " בהמשך הפרק נעבור להשתמש בתוכנות שיותר מתאימות למשימה הזו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי להתחיל לעבוד, הורידו מפה את קובץ מסד הנתונים לדוגמה שיצרנו עבורכם.
\n", + " היכנסו לאתר והעלו לשם את הקובץ שהורדתם.
\n", + " זהו מסד הנתונים שעליו נעבוד. הוא מכיל מידע מעניין על סרטים, כולל דירוגים, הצוות שהשתתף ביצירתם וכדומה.
\n", + " מייד בסיום ההעלאה יפתח לכם חלון שבראשו תיבת טקסט – בעזרתה תעלו את השאילתות שלכם, ובתחתיתו טבלה – בה יופיעו תוצאות השאילתה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## מילת המפתח SELECT" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נתחיל מלהכניס לתיבת הטקסט שאילתה פשוטה שתציג לנו את שמות כל הסרטים שקיימים במאגר:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT title\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כיוון שמאגר הנתונים שלנו גדול במיוחד, הממשק הגרפי ישאל אתכם אם אתם רוצים להציג את כל הנתונים.
\n", + " בחרו \"לא\" כדי למנוע קריסה של הלשונית.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשאילתה שתי מילות מפתח: SELECT ו־FROM.
\n", + " אחרי מילת המפתח FROM נכתוב שם של טבלה שממנה נרצה לשלוף את הנתונים.
\n", + " אחרי מילת המפתח SELECT נכתוב את שם העמודה שממנה נרצה לשלוף את הנתונים.
\n", + " את השאילתה נסיים בנקודה פסיק.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אפשר לתרגם את השאילתה באופן מילולי ל\"בחר את כל הכותרות מתוך הטבלה סרטים\".\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסו להכניס את השאילתה לאתר ולהריץ. מצליחים לזהות סרטים מוכרים?
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"טיפ!\"\n", + "
\n", + "
\n", + " ברוב מסדי הנתונים אין משמעות לבחירה באותיות גדולות או קטנות.
\n", + " מסדי נתונים לרוב גם יתעלמו משבירות שורה (\"אנטרים\") באמצע השאילתה.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נהוג לכנות את החלק בשאילתה שקשור ל־SELECT בשם \"פסוקית ה־SELECT\" (באנגלית: SELECT clause).
\n", + " באותה צורה, נהוג לכנות את החלק בשאילתה שקשור ל־FROM בשם \"פסוקית ה־FROM\" (באנגלית: FROM clause).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### חקר של מבנה מסדי נתונים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כיצד נדע אילו טבלאות קיימות במסד הנתונים, ואילו עמודות קיימות בכל טבלה?
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם אתם מכירים את שם הטבלה ורוצים לגלות מה העמודות שלה, יש טריק זריז וכייפי.
\n", + " במקום לבקש פרטים על עמודה מסוימת, אפשר לכתוב אחרי מילת המפתח SELECT את הסימן *.
\n", + " הסימן הזה יגיד למסד הנתונים שאנחנו רוצים לאחזר את כל העמודות בטבלה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסו אצלכם!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " מה שמות העמודות בטבלה principals?\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + "אבל מה אם אנחנו לא יודעים אילו טבלאות יש לנו במסד הנתונים?
\n", + " עומדות בפנינו עוד כמה אפשרויות:\n", + "

\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"אזהרה!\" \n", + "
\n", + "
\n", + "

\n", + " לפעמים יופיעו באינטרנט בדוגמאות לשאילתות SQL שנראות מעט שונה ממה שתלמדו פה.
\n", + " השוני נובע מכך שמנועים שונים של מסדי נתונים משתמשים לפעמים בתחביר מעט שונה של SQL,
\n", + " אבל בשורה התחתונה – זו אותה הגברת בשינוי אדרת.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### מבנה מסד הנתונים שלנו" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " זה הזמן להציג לכם כיצד נראה מסד הנתונים שיצרנו עבורכם!
\n", + " זה הולך להיראות קצת מוגזם בהתחלה, אבל אין צורך להיבהל. נסביר כל דבר כשיגיע הזמן הנכון.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בתמונה\n", + "
\n", + " תרשים שמציג את כל הטבלאות והעמודות במסד הנתונים, כולל את הקשרים ביניהם.
\n", + " המינוח המקצועי לתרשים שכזה הוא \"תרשים ישויות קשרים\" (באנגלית: ERD).\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " קחו את הזמן לחקור קצת את מסד הנתונים באמצעות SELECT ו־FROM.
\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### עוד קצת על SELECT" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אולי גיליתם כבר בעצמכם שאפשר לאחזר כמה עמודות מטבלה כלשהי במכה.
\n", + " נכתוב שאילתה שתחזיר לנו שתי עמודות מטבלת movies: את id ואת title.
\n", + " כדי לעשות זאת נפריד בין שמות העמודות באמצעות פסיק:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT id, title\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נוסיף שאפשר גם לשנות את שמן של עמודות ששלפתם עליהן, בעזרת מילת המפתח AS.
\n", + " מייד אחרי העמודה שאת שמה אתם רוצים לשנות, הוסיפו את מילת המפתח AS,
\n", + " ואחריה כתבו את השם החדש שתרצו לתת לעמודה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT id AS imdb_id, title AS english_name\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בדוגמה שלמעלה השתמשנו במילת המפתח AS.
\n", + " בעזרתה, הצגנו את העמודה id שבטבלת movies תחת השם החדש imdb_id.
\n", + " את title מאותה טבלה הצגנו תחת השם החדש english_name.
\n", + " יכולנו גם לבחור לתת רק לאחת מהעמודות שם חדש, או, כפי שעשינו קודם, לבחור לשלוף אותן בשמן המקורי.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"אזהרה!\" \n", + "
\n", + "
\n", + "

\n", + " שאילתות SELECT ככלל לא משנות מידע במסד הנתונים עצמו.
\n", + " הן רק מחזירות פלט לפי השאילתה שהעברתם למסד הנתונים.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"טיפ!\"\n", + "
\n", + "
\n", + " נסו לשמור על שמות העמודות שלכם ידידותיות כמה שאפשר.
\n", + " הימנעו משמות עמודות עם רווחים, מילים שמורות (כמו SELECT), תווים מיוחדים או מילים בעברית שמתועתקות לאנגלית.
\n", + " ודאו תמיד שבחרתם שם מוצלח – גם אם מסד הנתונים לא התריע על שגיאה.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### שמים גבול" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שליפה של נתונים רבים מדי עלולה להיות בעוכרינו.
\n", + " היא דורשת ממסד הנתונים לבצע פעולות מיותרות שלוקחות זמן,
\n", + " והעברת הנתונים באינטרנט ככל הנראה תאט מאוד את המהירות שבה נוכל לראות את תוצאות השאילתה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לשם כך באה לעזרתינו מילת המפתח LIMIT.
\n", + " היא מאפשרת לנו להגיד כמה רשומות נרצה לאחזר בעזרת השאילתה שכתבנו.
\n", + " המילה הזו תבוא בסוף השאילתה, ואחריה נציין את מספר הרשומות המירבי שנרצה לאחזר.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ננסה, לדוגמה, לשלוף מטבלת movies רק 100 סרטים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies\n", + "LIMIT 100;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " תוכלו להיווכח בעצמכם בשינוי המשמעותי במהירות שבה התוצאות חוזרות.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בחלק מהניבים השונים של SQL, אתם תראו שאנשים משתמשים במילה TOP במקום במילה LIMIT.
\n", + " זו אותה הגברת בשינוי אדרת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"אזהרה!\" \n", + "
\n", + "
\n", + "

\n", + " מילת המפתח LIMIT לא תחזיר בהכרח את אותן רשומות בכל פעם.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: איך כוכב אחד מעז" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו שאילתה שמציגה 100 אנשי מפתח מטבלת principals.
\n", + " השאילתה תציג את כל העמודות בטבלה.
\n", + " כתבו את השאילתה כך שבתוצאות העמודה characters תופיע בשם personas.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסו לפתור את התרגיל בלי להציץ בתיעוד שסיפקנו למעלה.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### עמודה מלאכותית" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אפשר יחסית בקלות ליצור עמודה נוספת שתופיע כחלק מתוצאות השאילתה שלנו.
\n", + " דוגמה די מטופשת תהיה, לדוגמה, להוסיף לצד כל העמודות של סרט מסוים עמודה שבה תמיד מופיעה הספרה 1.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *, 1\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " העמודה הזו תקרא כברירת מחדל \"1\", אבל כמו שלמדנו קודם, נוכל לתת לעמודה הזו שם:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *, 1 AS is_movie\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הדוגמה הזו אולי לא מחוכמת במיוחד, אבל היא הקדמה טובה להרבה דברים מעניינים שאפשר לעשות.
\n", + " יצירת עמודה חדשה מאפשרת לנו גמישות בהצגת הפלט מהשאילתה שלנו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ננסה להריץ ביטוי מעט יותר מורכב:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *, 17 + 5 * 5 AS universal_answer\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ונראה שמסד הנתונים יודע להתמודד גם עם ביטויים אריתמטיים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עד כה המשחקים הללו נראים נחמדים, אבל לא באמת מועילים.
\n", + " הכיף האמיתי מתחיל כשאנחנו גוזרים את העמודה המלאכותית החדשה שלנו מתוך נתונים קיימים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לדוגמה, בטבלה movies קיימת העמודה duration.
\n", + " כיצד הייתם יוצרים עמודה נוספת בשם duration_in_hours שבה נראה כמה שעות (ולא דקות) אורך הסרט?
\n", + " נסו להשיג את התוצאה בעצמכם, התשובה נמצאת בתא שפה למטה.
\n", + " הפתרון לאו דווקא מובן מאליו, ודורש מעט אינטואיציה טכנולוגית.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כך אנחנו עשינו זאת:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *, duration / 60.0 AS duration_in_hours\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בתא שלמעלה השתמשנו בעמודה duration.
\n", + " חילקנו את ערכה ב־60 וקראנו לערך שנוצר duration_in_hours.
\n", + " מסד הנתונים לקח בכל שורה את הערך שבעמודה duration, חילק אותו ב־60 והוסיף אותו כעמודה בשם שבחרנו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כל פעם שאנחנו מכניסים ביטוי כשדה חדש ב־SELECT,
\n", + " נוכל לדמיין שמסד הנתונים עובר שורה שורה ומחשב את הביטוי עבור השורה המסוימת הזו –
\n", + " ממש כמו בלולאה בפייתון!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עד כה התייחסנו לעמודות חדשות שבחרנו להוסיף בפסוקית ה־SELECT, בשם \"עמודות מלאכותיות\".
\n", + " השם שלהן בעגה המקצועית הוא \"עמודות מחושבות\" (Computed columns).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## מילת המפתח WHERE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " צורך בסיסי בעבודה עם כמות גדולה של נתונים הוא להתמקד באוסף נתונים שרלוונטי למשימה שלנו.
\n", + " לכן, נצטרך הרבה פעמים כלי שעוזר לנו לקבל רק שורות שעונות על תנאי מסוים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במקרים כאלו, מילת המפתח WHERE יכולה לעזור לנו מאוד.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נתחיל בשליפת כל העמודות של טבלת האנשים המפורסמים במסד הנתונים שלנו.
\n", + " נסו בעצמכם – הרשומות אודות אותם אנשים נמצאות תחת הטבלה names.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשלב הזה אתם אמורים להרגיש בנוח עם השליפה הזו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM names;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נניח שאנחנו מעוניינים לקבל את כל האושיות שגבוהם גדול מ־2 מטר.
\n", + " נוכל להשתמש במילת המפתח WHERE כדי לבצע את הסינון הזה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM names\n", + "WHERE height >= 200;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בתא האחרון הוספנו את פסוקית ה־WHERE לסוף השאילתה.
\n", + " הפסוקית הזו מאפשרת לנו לסנן את הנתונים לפי תנאי שיכתב בה.
\n", + " לדוגמה, אנחנו ביקשנו להחזיר רק שורות שבהן הערך שמופיע בעמודה height יהיה לפחות 200.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אפשר לנסח את השאילתה הזו במילים:
\n", + " בחר (SELECT) את כל העמודות, מתוך (FROM) הטבלה names, שבהן (WHERE) ערך העמודה height גדול או שווה ל־200.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כמו שאתם יכולים לנחש, סימני השיוויון שאתם מכירים מפייתון תופסים כאן: <=, <, > ו־>=.
\n", + " היוצאים מן הכלל הם הסימן = שמחליף את הסימן ==, והסימן <> שלרוב מופיע במקום !=.
\n", + " שימוש בסימן == ממש לא חוקי ויזרוק לכם שגיאה, ולעומתו הסימן != אומץ ברוב הניבים של SQL ויעבוד לכם כמעט תמיד.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"אזהרה!\" \n", + "
\n", + "
\n", + "

\n", + " SQL, כמו פייתון, רגישה לאותיות גדולות וקטנות כשמבקשים ממנה להשוות בין מחרוזות.
\n", + " בניגוד לפייתון, אפשר להשתמש רק בגרש (') כדי לבנות מחרוזת. גרשיים (\") יקיפו שם של טבלה או עמודה.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " בשנת 1994 יצא סרט דרמה אפי הונגרי מצליח בשם \"הטנגו של השטן\" (Sátántangó).
\n", + " מה אורכו של הסרט?\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### סדר הרצת פסוקיות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מעניין לדעת שמנוע ה־SQL לא מריץ את הפסוקיות בסדר שבו הן כתובות בשאילתה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ננתח את סדר הרצת הפסוקיות בשאילתה הפשוטה הבאה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM names\n", + "WHERE height >= 200\n", + "LIMIT 10;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " המנוע קודם יגש לטבלה ממנה הוא רוצה לשלוף (names), אותה ציינו בפסוקית ה־FROM.
\n", + " משם הוא קודם כל יברור את הנתונים לפי התנאי (height >= 200) שסיפקתם לו בפסוקית ה־WHERE,
\n", + " לאחר מכן הוא יבחר את כל העמודות הרלוונטיות להצגה (*), שמופיעות בפסוקית ה־SELECT,
\n", + " ולבסוף הוא יחזיר רק את כמות התוצאות שכתובה ב־LIMIT (במקרה שלנו – 10).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בתמונה:\n", + "
\n", + " איור שממחיש את סדר הרצת הפסוקיות בשאילתת SQL שמבצע מנוע ה־SQL על מסד הנתונים.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " חלק ממנועי ה־SQL יתריעו על שגיאה כשתנסו להריץ שאילתה שלא תואמת את הסדר הזה.
\n", + " לדוגמה, כמעט כל מנוע שהוא לא SQLite (המנוע בו אנחנו משתמשים כרגע) יתריע על שגיאה בשאילתה הבאה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT height / 100 AS meters\n", + "FROM names\n", + "WHERE meters >= 2\n", + "LIMIT 10;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כשמנוע ה־SQL מגיע לפסוקית ה־WHERE, הוא טרם הריץ את פסוקית ה־SELECT.
\n", + " לכן הוא לא מכיר את העמודה המחושבת meters, ותיזרק לכם שגיאה.
\n", + " SQLite פותר את הבעיה הזו בכך שמאחורי הקלעים הוא מעתיק את הביטויים בעמודות מחושבות לתוך פסוקית ה־WHERE.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### אופרטורים לוגיים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ודאי לא יפתיע אתכם שאופרטורים לוגיים קיימים גם ב־SQL –
\n", + " מדובר על אותם AND, OR ו־NOT שהתרגלנו לכתוב בביטויים בוליאניים בפייתון.
\n", + " נוכל לכתוב אותם בפסוקיות WHERE כדי ליצור תנאים מורכבים ומעניינים יותר.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כך, לדוגמה, נוכל לבקש סרט אחד שיצא בשנות ה־50:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies\n", + "WHERE year >= 1950 AND year < 1960 \n", + "LIMIT 1;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשלב הזה אנחנו מניחים שהעמקתם ואתם בקיאים בניסוח ביטויים בוליאניים,
\n", + " ולכן לא נתעכב על הנושא הזה במחברת הזו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: Sir Etzar" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " תחביבו של סר. אטזר הוא לראות סרטים בשפת המקור.
\n", + " חפשו עבורו סרטים ששמם בשפת המקור שונה מהשם הפופולרי שלהם.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " חשוב לציין שסר. אטזר הוא בחור עסוק למדי, ולכן יוכל לצפות רק ב־5 סרטים שאורך כל אחד מהם הוא פחות משעה וחצי.
\n", + " שלפו את הכמות הנכונה של סרטים שמתאימים לדרישותיו של סר. אטזר.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### אופרטור ההשוואה LIKE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אופרטור השוויון ב־SQL (=), כמו בפייתון, מניח שהנתון שמופיע מצידו הימני שווה לחלוטין לנתון שמופיע בצידו השמאלי.
\n", + " בשימוש שוטף במסדי נתונים, לא יעבור זמן רב עד שנגלה שהכלי הזה לא מספיק חזק כדי לענות על כל הצרכים שלנו.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מה אם נרצה לחפש בתוך מלל ב\"בערך\", ולא בהכרח לחפש שיוויון מוחלט?
\n", + " זה קורה לעיתים כל כך תכופות!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הנה כמה מצבים כאלו שחשבנו עליהם:\n", + "

\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מטרת אופרטור ההשוואה LIKE היא לפתור עבורנו את הבעיה הזו.
\n", + " הוא יכתב כחלק מפסוקית ה־WHERE, ויאפשר לנו לחפש תבניות.
\n", + " נראה דוגמה לשימוש בו, ואז נסקור כיצד להשתמש בו במקרים נוספים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies\n", + "WHERE title LIKE '%Sherlock%';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בתא שלמעלה השתמשנו באופרטור ההשוואה LIKE
\n", + " דרשנו לקבל את כל השורות בהן מתקיים התנאי title LIKE '%Sherlock%'.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כשאנחנו משתמשים באופרטור ההשוואה LIKE אנחנו מבקשים לבחון האם הביטוי בצד שמאל תואם את התבנית שנמצאת בימין.
\n", + " במקרה שלנו, ביקשנו לבדוק בכל שורה אם הערך שב־title תואם את התבנית '%Sherlock%'. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הכללים לניסוח תבנית פשוטים מאוד (פה קורה הקסם!):\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כך שהתבנית שניסחנו אומרת \"כל מחרוזת הכוללת את המילה Sherlock\".
\n", + " באותה מידה, יכולנו להשתמש ב־'Sherlock%' כדי לקבל רק את השורות שמתחילות במילה Sherlock.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " תוכלו לשלוף את כל הסרטים שאורך שמם הוא בדיוק 5 תווים?\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"טיפ!\"\n", + "
\n", + "
\n", + " חבר קרוב של אופרטור ההשוואה LIKE הוא NOT LIKE.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"אזהרה!\" \n", + "
\n", + "
\n", + "

\n", + " מבחינה טכנית, אפשר להשתמש ב־LIKE בשביל השוואה פשוטה – אם במקום תבנית נכתוב לימינו מחרוזת רגילה.
\n", + " זה נחשב לא מנומס ואין סיבה טובה לעשות את זה.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### הערך NULL" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " NULL הוא קבוע מיוחד, והוא קיים כדי לייצג חוסר בערך במסד הנתונים שלנו.
\n", + " הוא מזכיר למדי את הערך הפייתוני None, שגם הוא מייצג ריק, שום דבר.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למרבה ההפתעה, המצב של הופעת הערך NULL הוא נפוץ יחסית.
\n", + " חשבו על כל הפעמים שיתכן שסיפקו לנו מידע חסר, או על המצבים שבהם אין מידע למלא בעמודה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " חשוב לחדד ש־NULL לא מציין אפס – הוא מציין שהמידע פשוט לא נמצא שם.
\n", + " אם אני מריץ שאילתה שמטרתה לבדוק כמה מקררים יש במלאי ומקבל בחזרה את התשובה NULL,
\n", + " אני יודע שלמרות שיש מקררים במסד הנתונים, לא עודכן המלאי עבורם.
\n", + " NULL לא אומר שאין מקררים במלאי – מצב שכזה היה מיוצג על ידי הערך 0.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הבעיה מתחילה בכך שהערך NULL ב־SQL מתנהג קצת מוזר.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ננסה להריץ שאילתה שמחזירה את כל הסרטים שלא ידוע מה היה תקציב ההפקה שלהם:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies\n", + "WHERE budget = NULL;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למרבה ההפתעה, השאילתה הזו לא מחזירה לנו אף סרט.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " טוב, אם כך, כנראה שאנחנו יודעים מה היה תקציבם של כל הסרטים שבמסד הנתונים שלנו. כמה נפלא.
\n", + " נשלוף את כל הסרטים שבהכרח ידוע לנו מה היה תקציב ההפקה שלהם:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies\n", + "WHERE budget <> NULL;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " גם הפעם השאילתה לא החזירה לנו שורות כלל! איזה משונה, אנחנו יודעים שבטבלה יש סרטים!
\n", + " הסרטים שתקציב ההפקה שלהם = NULL והסרטים שתקציב ההפקה שלהם <> NULL, שני ההפכים המוחלטים, לא מחזירים אף שורה.
\n", + " מה מתרחש פה?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מתברר שב־SQL נוסף לערכים הלוגיים שאנחנו רגילים אליהם (True, False), ישנו ערך לוגי שלישי: Unknown.
\n", + " כך הוגדר שתוצאת בדיקת השוויון ל־NULL היא תמיד Unknown. הוא הדין גם לגבי בדיקת אי־שוויון.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כיוון שמנוע ה־SQL יחזיר רק שורות שעבורן הביטוי הלוגי בפסוקית ה־WHERE שקול ל־True,
\n", + " בדיקת שוויון רגילה עם NULL, שתוצאתה Unknown, תגרום למנוע לא להחזיר את השורות שציפינו לקבל.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נראה לדוגמה את המקרה המובהק הבא.
\n", + " על פניו, כל ערך שווה לעצמו ולכן אמורות לחזור לנו כל השורות מהטבלה.
\n", + " בפועל, כיוון שהשוואה ל־NULL תמיד תחזיר Unknown, מנוע ה־SQL לא יחזיר לנו אף שורה מהטבלה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies\n", + "WHERE NULL = NULL;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אז מה אפשר לעשות אם נרצה בכל זאת לבצע השוואה ל־NULL?
\n", + " נוכל להשתמש באופרטור ההשוואה IS ולבדוק האם ערך מסוים IS NULL או IS NOT NULL.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נבדוק, אם כן, אילו בעלי תפקידים בכירים שיחקו גם דמויות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM principals\n", + "WHERE characters IS NOT NULL;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עכשיו, כשאתם יודעים איך NULL מתנהג, חשוב להיזהר ממצבים שבהם הוא עלול להציק לנו.
\n", + " כשנכתוב שאילתה לאחזור כל האנשים שגובהם הוא לא בדיוק 2 מטר,
\n", + " נצטרך לציין במפורש את המקרה בו הגובה שלהם הוא NULL:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM names\n", + "WHERE height != 200\n", + " OR height IS NULL;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## סיכום" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במחברת זו צללנו לראשונה לעולם של SQL ולמדנו את היסודות לכתיבת שאילתות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הבנו מה זה מסד נתונים ומתי משתמשים בו, וסקרנו לראשונה את הרעיון של אחזור מידע ממסד הנתונים.
\n", + " ראינו כיצד לאחזר מידע די מעניין בעזרת שאילתות פשוטות יחסית, שמורכבות מהפסוקיות SELECT, FROM, WHERE ו־LIMIT.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למדנו גם על אופרטורי השוואה וביניהם LIKE, ופגשנו שוב את AND, OR ו־NOT – הפעם בהקשרי SQL.
\n", + " קינחנו בלהבין מי זה NULL ועל המוזרויות שבהתעסקות איתו – ושמחייבות אותנו להשתמש ב־IS NULL ולא לבצע השוואה ישירה מולו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## מונחים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
מסד נתונים (Database)
\n", + "
\n", + " אוסף מסודר של מידע שלרוב מאוחסן במכשיר אלקטרוני, ולעיתים קרובות מנוהל על ידי תוכנה ממוחשבת.\n", + "
\n", + "
המנוע (Database Management System)
\n", + "
\n", + " תוכנה ממוחשבת שמאפשרת למשתמשים בה להגדיר, לתחזק ולאחזר מידע ממסד הנתונים.\n", + "
\n", + "
עמודה (Column)
\n", + "
\n", + " נקרא גם תכונה (attribute) או שדה (field).
\n", + " הגדרה מופשטת של תכונה מסוימת שמשותפת למספר עצמים. לתכונה יש סוג נתונים (טיפוס) והגבלות שיכולות לחול עליה.
\n", + " לדוגמה – התכונה \"תאריך יום הולדת\" תהיה מסוג תאריך, ותחול עליה ההגבלה שהערך בה לא יכול להיות בעתיד.\n", + "
\n", + "
שורה (Row)
\n", + "
\n", + " גם רשומה (entry) או ישות (entity).
\n", + " עצם המורכב מאוסף של תכונות ומוגדר לפיהן.
\n", + " לדוגמה – משתמש במערכת ניהול לקוחות יהיה בעל התכונות: שם, טלפון, מספר תעודת זהות ותאריך יום הולדת.\n", + "
\n", + "
טבלה (Table)
\n", + "
\n", + " קיבוץ של 0 ישויות או יותר, שלכל אחת מהן יש אותן תכונות.\n", + "
\n", + "
מסד נתונים רלציוני (Relational Database)
\n", + "
\n", + " סוג של מסד נתונים שבו כל ישות (\"שורה\") מורכבת מקבוצת תכונות (\"עמודות\").
\n", + " ישויות עם אותן תכונות מקובצות לרוב תחת טבלה אחת.\n", + "
\n", + "
שאילתה (Query)
\n", + "
\n", + " קוד שמטרתו לקבל מידע ממסד נתונים, או לבצע שינויים במסד הנתונים.
\n", + " השאילתה נכתבת בשפה שנקראת SQL – שפת שאילתות מובנית.\n", + "
\n", + "
פסוקית (Clause)
\n", + "
\n", + " חלק משאילתת ה־SQL שכולל מילת מפתח ואת החלקים שמתייחסים אליה.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## תרגילים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תמיד ביחד?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " החזירו את כל הסרטים שיש בהם 2 כספרה, ללא ספרות או תווים נוספים שצמודים אליה.
\n", + " אל תחזירו סרטים שבהם יש את הספרה 2 כחלק ממספר גדול יותר. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לדוגמה: החזירו את הסרט Shrek 2, אך לא את הסרט 12 Angry Men.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### דירוג משוכלל" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " \"דירוג משוכלל\" הוא הדירוג של סרט, לאחר שמוסיפים לו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לדוגמה, סרט שדורג 4.2 על ידי 120,000 מצביעים, ידורג בפועל לפי הנוסחה הבאה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$rating = 4.2 + (0.2 \\cdot 1) + (0.01 \\cdot 2) + (0.0005 \\cdot 0) = 4.420$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בהינתן שדירוג יכול לנוע מ־0 ועד 10 נקודות, האם שיטת הדירוג הזו יוצרת סרטים שהדירוג שלהם לא תקין?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### And the science gets done" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " החזירו את שמותיהם של 3 בעלי תפקידים שעדיין בחיים ולהם לפחות תשעה ילדים.\n", + "

" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week13/2_Order_and_functions.ipynb b/week13/2_Order_and_functions.ipynb new file mode 100644 index 0000000..9ba8da8 --- /dev/null +++ b/week13/2_Order_and_functions.ipynb @@ -0,0 +1,1125 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"לוגו" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# כתיבת שאילתות – חלק 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עד כה למדנו על הפסוקיות SELECT, FROM, WHERE ו־LIMIT ועל סדר הרצתם על ידי מנוע ה־SQL.
\n", + " דיברנו גם על אופרטורי השוואה, אופרטורים לוגיים ועל NULL,
\n", + " ובעצם למדנו לראשונה מה הם מסדי נתונים ואיך כותבים שאילתות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במחברת זו נלמד:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "
  1. איך מסדרים את התוצאות שחזרו מהשאילתה שלנו.
  2. \n", + "
  3. מהן הפונקציות המובנות ב־SQL ובאילו מקרים הן עשויות לסייע לנו.
  4. \n", + "
  5. כיצד למנוע כפילויות בתוצאות השאילתה.
  6. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בואו נתחיל!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ORDER BY" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כיוון שסידור של נתונים היא פעולה שאורכת זמן רב, כברירת מחדל מסדי נתונים לא מחזירים תוצאות לפי סדר מסוים.
\n", + " ובכל זאת, לפעמים עולה הצורך לקבל את התוצאות כשהן מסודרות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אז איך נבקש ממסד הנתונים לסדר עבורינו את התוצאות?
\n", + " נוסיף פסוקית ORDER BY לשאילתה, ואחריה נציין את שם העמודה לפיה אנחנו רוצים לסדר:
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM names\n", + "ORDER BY children;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשאילתה האחרונה, ביקשנו לסדר את הישויות בטבלת names לפי מספר הילדים שיש ברשותם.
\n", + " נוכל גם להוסיף פסוקית LIMIT כדי להגביל את כמות התוצאות שיחזרו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM names\n", + "ORDER BY children\n", + "LIMIT 5;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כך, בשאילתה האחרונה, הוחזרו לנו רק 5 אנשים ממסד הנתונים.
\n", + " אלו האנשים שכמות הילדים שהביאו לעולם היא הנמוכה ביותר.
\n", + " זה המצב כיוון שמסדי נתונים מסדרים את התוצאות, כברירת מחדל, בסדר עולה – מהנמוך לגבוה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נוסיף גם פסוקית WHERE, שתחזיר עבורינו רק אנשים שעודם בחיים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM names\n", + "WHERE date_of_death IS NULL\n", + "ORDER BY children\n", + "LIMIT 5;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עכשיו, כשאנחנו יודעים איך נראית שאילתה שבנויה מכל הפסוקיות שלמדנו,
\n", + " נרענן את סדר הרצת הפסוקיות במנוע ה־SQL:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בתמונה:\n", + "
\n", + " איור שממחיש את סדר הרצת הפסוקיות בשאילתת SQL שמבצע מנוע ה־SQL על מסד הנתונים.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " מה שמו של בעל התפקיד הנמוך ביותר במסד הנתונים שלנו? מה גובהו?
\n", + " לפני שתפתרו, חישבו על המוקש שצריך לנטרל בכתיבת שאילתה שכזו.\n", + "

\n", + "
\n", + "
\n", + "

\n", + " חשוב!
\n", + " פתרו לפני שתמשיכו!\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי לפתור את השאלה למעלה, נפרק אותה למרכיביה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ננסח כשאילתה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT height, name\n", + "FROM names\n", + "WHERE height IS NOT NULL\n", + "ORDER BY height\n", + "LIMIT 1;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " במחברת הקודמת למדנו שרוב המנועים של מסדי הנתונים לא מאפשרים להשתמש בעמודות מחושבות בפסוקית ה־WHERE.
\n", + " האם אפשר להשתמש בעמודות מחושבות בפסוקית ה־ORDER BY? מדוע? \n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### סדר שונה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כפי שלמדנו לפני כן, ברירת המחדל של ORDER BY היא לסדר את העמודה מהתוצאה הקטנה ביותר לגדולה ביותר.
\n", + " אבל מה אם אנחנו רוצים לסדר את התוצאות בסדר הפוך?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למזלנו יש פתרון פשוט.
\n", + " אחרי שם העמודה בפסוקית ORDER BY, נציין אם היא תסודר בסדר עולה או יורד.
\n", + " נבחר במילת המפתח DESC (מלשון descending, יורד) אם נרצה שהנתונים בעמודה יסודרו מהגדול לקטן,
\n", + " או ASC (מלשון ascending, עולה) אם נרצה להצהיר במפורש שהנתונים בעמודה יסודרו מהקטן לגדול. זו גם ברירת המחדל.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נבקש לסדר את שמות בעלי התפקידים לפי הגובה שלהם – מהגבוה לנמוך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT name\n", + "FROM names\n", + "WHERE height IS NOT NULL\n", + "ORDER BY height DESC;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"העמקה\" \n", + "
\n", + "
\n", + "

\n", + " אם נחליט שלא לסנן החוצה את ערכי ה־NULL, נוכל להחליט אם הם יופיעו ראשונים או אחרונים.
\n", + " בפסוקית ה־ORDER BY, נכתוב NULLS FIRST כדי שיוחזרו ראשונים, או NULLS LAST כדי שיוחזרו אחרונים.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### סידור לפי עמודות מרובות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נשפר מעט את השאילתה שכתבנו זה עתה.
\n", + " בשביל פישוט הדוגמה הבאה, נסנן החוצה את הרשומות שמכילות גבהים לא הגיוניים, ונבקש לראות רק 100 תוצאות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT * \n", + "FROM names \n", + "WHERE height IS NOT NULL \n", + " AND height < 250 \n", + "ORDER BY height DESC \n", + "LIMIT 100;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ואולי, אחרי מחשבה נוספת, יהיה הולם לתת כבוד לבעלי התפקידים המבוגרים יותר.
\n", + " מובן שכדי לסדר את האנשים לפי תאריך הלידה שלהם, נצטרך להשתמש ב־ORDER BY, בצורה הבאה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT * \n", + "FROM names \n", + "WHERE date_of_birth IS NOT NULL \n", + "ORDER BY date_of_birth -- ASC אם נרצה, אפשר להוסיף כאן את מילת המפתח\n", + "LIMIT 100;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אבל מה אם נרצה לסדר את בעלי התפקידים לפי גובהם,
\n", + " ורק אם גובהם של 2 בעלי תפקידים או יותר זהה לתת עדיפות לזה שנולד קודם?
\n", + " מקרה שכזה מכריח אותנו להתייחס ל־2 עמודות בפסוקית ה־ORDER BY: גם height וגם date_of_birth.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נכתוב את העמודות בפסוקית ה־ORDER BY לפי החשיבות שלהן, ונפריד ביניהן בפסיק:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "ORDER BY height DESC, date_of_birth\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ובתוך השאילתה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT * \n", + "FROM names \n", + "WHERE height IS NOT NULL \n", + " AND height < 250\n", + " AND date_of_birth IS NOT NULL \n", + "ORDER BY height DESC, date_of_birth\n", + "LIMIT 100;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " דרך טובה להסתכל על התוצאות שיחזרו היא \"סדר לפי גובה בסדר יורד – ואם הגובה זהה, סדר לפי תאריך הלידה\".\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שימו לב שבשאילתה הזו בחרנו להשמיט את כל מי שאין לנו ידע לגבי גובהו או לגבי מתי הוא נולד.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: ועדת המדרוג" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כשבנו את מסד הנתונים עליו אתם עובדים, החליטו להסיר ממנו את כל הסרטים שדורגו מעט מדי פעמים.
\n", + " מצאו מה המספר הקטן ביותר של דירוגים שסרט היה צריך לקבל כדי להישאר במסד הנתונים.
\n", + " איזה סרט קיבל את הציון הממוצע הגבוה ביותר עם מספר דירוגים שכזה?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## פונקציות במסדי נתונים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לשמחתנו הרבה, כמו שישנן פונקציות מובנות בפייתון – קיימות כאלו גם במסדי נתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ישנן לא מעט כאלה, וכמובן שלא נסקור את כולן –
\n", + " אבל בכל זאת בחרנו להתעכב על כמה שימושיות שיסייעו לנו בדוגמאות בהמשך.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"אזהרה!\" \n", + "
\n", + "
\n", + "

\n", + " רוב תחביר ה־SQL שלמדנו עד עכשיו די דומה בין מנועי ה־SQL השונים.
\n", + " בניגוד לתחביר שלמדנו, נדיר למצוא פונקציות ששמותיהם זהים בכל המנועים השונים.
\n", + " אנחנו ממליצים מאוד לרפרף בפרק הפונקציות שנמצא בתיעוד של המנוע בו אתם משתמשים.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### חיתוך מחרוזות באמצעות substr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מטרת הפונקציה substr היא להחזיר חלק מהתווים של מחרוזת מסוימת. היא עובדת באופן מעט שונה מחיתוך בפייתון.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הפונקציה מקבלת שלושה פרמטרים:
\n", + "

\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לדוגמה, הביטוי substr(\"Piposh\", 1, 3) יחזיר את המחרוזת \"Pip\", כיוון שהוא חותך 3 תווים החל מהמקום הראשון במחרוזת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + "כמה דברים מעניינים מתוך התיעוד שכדאי לשים לב אליהם:\n", + "

\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסו לחזות מה יהיו תוצאותיהן של השאילתות הבאות.
\n", + " הריצו את השאילתות כדי לבדוק אם צדקתם.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT substr('Piposh', 1, 2);\n", + "SELECT substr('Piposh', -3, 2);\n", + "SELECT substr('Piposh', 1, -3);\n", + "SELECT substr('Piposh', -3, -3);\n", + "SELECT substr('Piposh', -3);\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### עיגול מספרים באמצעות round" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הפונקציה round(X, Y) מעגלת את המספר X ל־Y ספרות אחרי הנקודה העשרונית.
\n", + " אם הארגומנט Y לא מועבר לפונקציה, SQLite לא ישאיר ספרות אחרי הנקודה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + "לדוגמה:\n", + "

\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### טיפול בתאריכים באמצעות strftime" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מטרת הפונקציה strftime היא להחזיר תאריך לפי תבנית כלשהי.
\n", + " זו פונקציה יחסית מורכבת, לכן מומלץ שלא להסתפק בסיכום שבמחברת, וכן לגשת לקרוא את עמוד התיעוד המלא.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הפרמטר הראשון שנעביר לפונקציה יהיה מחרוזת, הבנויה כתבנית שמתארת כיצד יורכב התאריך שנרצה להחזיר כפלט.
\n", + " האפשרויות הן:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
אפשרויות לחלקי זמן עבור הפרמטר הראשון של strftime
המחרוזתהחלק שהיא מסמנת בתאריךדוגמה לערך חזרה אפשרי מ־strftime
%Yשנה בת 4 ספרות1812
%mחודש ב־2 ספרות12 (דצמבר)
%dיום בחודש ב־2 ספרות14
%Hשעה ב־2 ספרות, בין 00 ל־2410
%Mדקות50
%Sשניות00
%fשניות, כולל מילישניות עם 3 ספרות אחרי הנקודה03.141
%sמספר השניות שעברו מאז 1970-01-011234567890 (ב־13 בפברואר 2009, בשעה 23:31:30)
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " התבנית יכולה להיות בנויה מכמה חלקי זמן.
\n", + " המחרוזת '%Y-%m-%d', לדוגמה, מתארת תאריך המורכב משנה, חודש ויום, כשביניהם מפריד הסימן מקף.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הפרמטר השני הוא תיאור התאריך שאליו אנחנו רוצים להתייחס.
\n", + " לעיתים קרובות נראה שם את המחרוזת 'now' בתור הפרמטר, כדי להתייחס לתאריך הנוכחי.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסו בעצמכם:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT strftime('%Y-%m-%d %H:%M:%S', 'now') AS current_date;\n", + "-- Output: 2020-11-05 00:34:10\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הטריק המגניב בפונקציה הזו הוא שאפשר להעביר ארגומנטים נוספים (ראו modifiers בתיעוד) כדי לזוז קדימה ואחורה בזמן.
\n", + " לדוגמה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT strftime('%Y-%m-%d %H:%M:%S', 'now', 'start of month','+1 month','-1 day') AS current_date;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " יחזיר לי את היום אחרון בחודש הנוכחי.
\n", + " זה עובד כיוון ש־SQLite עקב אחרי ההוראות שנשלחו כפרמטרים ל־strftime:
\n", + " מצא את התאריך כרגע, זוז ממנו לתחילת החודש, קח חודש אחד קדימה ותפחית מזה יום.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: ארוך וקולע" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הציגו את 10 הסרטים ששמם הוא הארוך ביותר במסד הנתונים.
\n", + " ודאו שהסרט הראשון שמוצג הוא זה עם השם הארוך ביותר.
\n", + " אם יש שני סרטים שאורך שמם זהה, סדרו אותם לפי שמם בסדר אלפבתי.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " רמז: השתמשו בתיעוד הפונקציות באתר של SQLite.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## מילת המפתח DISTINCT" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ננסה להציג את השנים שבהן שוחררו הסרטים שנמצאים במסד הנתונים שלנו.
\n", + " בעזרת הכלים שרכשנו עד כה, נוכל בקלות לנסח שאילתה כזו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT \"year\"\n", + "FROM movies\n", + "ORDER BY \"year\";\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"טיפ!\"\n", + "
\n", + "
\n", + " כיוון ש־year היא גם שם של פונקציה ב־SQLite, בחרנו להקיף את שם העמודה בגרשיים.
\n", + " שם שמופיע בגרשיים (להבדיל מגרש, שמסמל מחרוזות) הוא בהכרח שם של טבלה או של עמודה.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " באופן לא מפתיע, שנים מסוימות מופיעות שוב ושוב בתוצאות – כמספר הסרטים ששוחררו באותה שנה.
\n", + " שנת 1934 חוזרת על עצמה 289 פעמים, לדוגמה, ושנת 2017 חוזרת על עצמה לא פחות מ־3329 פעמים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי להסיר את הכפילויות מתוצאות השאילתה נוכל להשתמש במילת המפתח DISTINCT.
\n", + " נעשה זאת כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT DISTINCT \"year\" \n", + "FROM movies\n", + "ORDER BY \"year\";\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הסרת הכפילויות תתבצע עבור כל העמודות שעליהן אנחנו שולפים בשאילתת ה־SELECT DISTINCT.
\n", + " זאת אומרת שאם נבקש לבצע DISTINCT על שתי עמודות או יותר,
\n", + " כל טור בפני עצמו יוכל להכיל כמה פעמים את אותו ערך – אבל שורה שלמה עם אותם ערכים לעולם לא תחזור על עצמה. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"אזהרה!\" \n", + "
\n", + "
\n", + "

\n", + " מילת המפתח DISTINCT חייבת להופיע מייד אחרי מילת המפתח SELECT.
\n", + " כרגע אין לנו דרך לבקש ממסד הנתונים להסיר כפילות רק בחלק מהעמודות.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## תרגיל לדוגמה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### שאלה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו שאילתה שתציג, ללא כפילויות, את כל השנים והחודשים בהם נולדו ידוענים שנמצאים במסד הנתונים שלנו.
\n", + " לדוגמה, במסד הנתונים תהיה שורה בה החודש הוא 05 והשנה היא 1899.
\n", + " סדרו את התוצאות לפי השנה ואז לפי החודש, בסדר עולה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסו לפתור את התרגיל בעצמכם לפני שתביטו בפתרון.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### פתרון" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " השאלה מתייחסת לתאריך הלידה (date_of_birth) של ידוענים (names),
\n", + " ומבקשת להשיג את שנת הלידה שלהם ואת חודש הלידה שלהם.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נוכל להשיג את הנתונים האלו בעזרת הפונקציה strftime,
\n", + " וכמובן שלא נשכח לוודא שאנחנו משתמשים אך ורק בשורות בהן date_of_birth ידוע לנו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כך אנחנו פתרנו את השאלה הזו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT DISTINCT strftime('%m', date_of_birth) AS month_of_birth, \n", + " strftime('%Y', date_of_birth) AS year_of_birth \n", + "FROM names\n", + "WHERE date_of_birth IS NOT NULL\n", + "ORDER BY year_of_birth, month_of_birth;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## סיכום" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במחברת זו רכשנו כלים נוספים שמאפשרים לנו לכתוב שאילתות SQL עשירות יותר.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למדנו כיצד לקבל את הנתונים בצורה מסודרת בעזרת ORDER BY.
\n", + " בהמשך, גילינו שקיימות פונקציות גם במסדי נתונים, ואלו מעשירות מאוד את היכולת שלנו לעבד מידע לפני שהוא חוזר אלינו.
\n", + " לבסוף, למדנו על מילת המפתח DISTINCT – שמאפשרת לנו לסנן החוצה תוצאות כפולות על עמודה אחת או יותר.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## תרגילים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### דקות של תהילה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כמה זמן אפשר להקדיש ללמידת פייתון? נמאס!
\n", + " וכל הדיבור הזה על סרטים...\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מפה לשם, החלטתם לפרוש לעסקי הבימוי.
\n", + " השתמשו במסד הנתונים כדי להחליט מהו האורך המיטבי עבור הסרט החדש שלכם.
\n", + " צרו עמודה בשם rating בה יופיע הציון המעוגל של הסרט, כמספר שלם.
\n", + " צרו עמודה נוספת בשם hours שמכילה את אורך הסרט בשעות, גם הפעם מעוגל למספר שלם.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מצאו כמה שעות צריך להיות הסרט שלכם כך שיהיה לו הסיכוי הטוב ביותר לקבל ציון גבוה.
\n", + " אל תציגו פעמיים סרטים באותו אורך שקיבלו את אותו ציון.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### שוברי קופות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו שאילתה שמחזירה את שמות כל הסרטים שגרפו את הרווח הגדול ביותר במסד הנתונים שלנו.
\n", + " הציגו רק סרטים שיש לנו את הנתונים הללו עליהם, ולא יותר מ־1,000 סרטים בסך הכל.
\n", + " אם שני סרטים גרפו רווחים זהים, סדרו אותם לפי היחס בין הרווחים שגרפו לבין התקציב שלהם.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " זו שאלה לא קלה שכוללת ישום אינטנסיבי של מה שלמדנו עד כה, ומעט חשיבה יצירתית.
\n", + " ודאו שאתם מתייחסים לטיב הנתונים לפני שאתם משתמשים בהם. אל תתייחסו לסרטים שרווחיהם שמורים במטבע שאינו דולרי.
\n", + " השתמשו בתיעוד הפונקציות של SQLite כדי לצלוח את השאלה. במידת הצורך, קראו גם על הפונקציה CAST.\n", + "

" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week13/3_Aggregations.ipynb b/week13/3_Aggregations.ipynb new file mode 100644 index 0000000..6757f51 --- /dev/null +++ b/week13/3_Aggregations.ipynb @@ -0,0 +1,1735 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"לוגו" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# כתיבת שאילתות אגרגטיביות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עד כה למדנו על הפסוקיות SELECT, FROM, WHERE, ORDER BY, ו־LIMIT, ועל סדר הרצתם על ידי מנוע ה־SQL.
\n", + " דיברנו על אופרטורי השוואה, אופרטורים לוגיים, על NULL ועל טיפול בכפילויות בעזרת מילת המפתח DISTINCT.
\n", + " גילינו גם על פונקציות במסדי נתונים, ועל איך הן מאפשרות לנו לעבד את התוצאות ולהחזיר תוצאות שנוח יותר לעבוד איתן.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במחברת זו נלמד:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "
  1. כיצד אפשר לבצע חישובים על ערכים משורות שונות.
  2. \n", + "
  3. כיצד אפשר לקבץ ישויות ולבצע חישובים בין הישויות בכל קבוצה.
  4. \n", + "
  5. כיצד לסנן תוצאות לפי תוצאות הקיבוץ.
  6. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בואו נתחיל!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## פונקציות אגרגטיביות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " פונקציות אגרגטיביות (באנגלית: Aggregate functions) הן פונקציות שפועלות על כמה שורות יחד, ומחזירות מהן ערך יחיד.
\n", + " הן מוכרות בעברית גם בשמות פונקציות צבירה, פונקציות קבוצה ופונקציות קיבוציות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הנה כמה דוגמאות למקרים שבהם פונקציות אגרגטיביות יכולות לסייע לנו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עד כה עבדנו עם פונקציות סקלריות – פונקציות שמקבלות רשימה של נתונים, ועבור כל איבר ברשימה מחזירות ערך.
\n", + " פונקציות אגרגטיביות הן פונקציות שמקבלות נתונים – ומחזירות תוצאה אחת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ניקח לדוגמה את הפונקציה \"קבל מספר, ותחזיר את אותו מספר בחזקת שתיים\".
\n", + " זוהי פונקציה סקלרית שבהינתן הנתונים 1, 2 ו־3 תחזיר 1, 4 ו־9 בהתאמה.
\n", + " שיעור שעבר למדנו על הפונקציה הסקלרית round, לדוגמה, שמקבלת מספר ומעגלת אותו.
\n", + " עבור הנתונים 3.1, 3.4 ו־3.7 היא תחזיר 3, 3 ו־4 בהתאמה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " פונקציות אגרגטיביות, כאמור, מקבלות רשימה של נתונים ומחזירות תוצאה אחת.
\n", + " הפונקציה \"קבל מספרים והחזר את סכומם\", לדוגמה, היא פונקציה אגרגטיבית, שבהינתן הנתונים 1, 2 ו־3 תחזיר 6.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ננסה להבין, לדוגמה, מה הדירוג הממוצע של סרט במסד הנתונים שלנו.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נוכל להשתמש בפונקציה האגרגטיבית AVG, שמקבלת קבוצת נתונים (במקרה שלנו: עמודה).
\n", + " הפונקציה תחזיר את ממוצע הערכים שאינם NULL באותה קבוצת נתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נבקש מהפונקציה לפעול על עמודת הציונים, avg_vote, בטבלת movies.
\n", + " הפונקציה תחזיר ערך יחיד: מה הציון הממוצע של סרט במסד הנתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT AVG(avg_vote)\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם הרצתם את השאילתה על מסד הנתונים שלכם, קיבלתם שורה אחת, בה יש ערך שקרוב ל־5.9.
\n", + " זהו הדירוג הממוצע של סרט במסד הנתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " פונקציות אגרגטיביות זכו לדף משלהם בתיעוד של SQLite.
\n", + " אתם עשויים לראות לעיתים קרובות שימוש בפונקציות SUM, MIN, MAX, COUNT ו־AVG.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " נסו לגלות כמה ערכי NULL נמצאים בעמודה budget שבטבלת movies.
\n", + " השתמשו בתיעוד.\n", + "

\n", + "
\n", + "
\n", + "

\n", + " חשוב!
\n", + " פתרו לפני שתמשיכו!\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הפונקציה COUNT, לדוגמה, מאפשרת לנו לספור כמה ישויות קיימות בטבלה מסוימת.
\n", + " נבדוק כמה סרטים קיימים לנו במסד הנתונים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT COUNT(*)\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שימו לב ש־COUNT לא סופרת ערכי NULL,
\n", + " כך שאם נעביר לה כארגומנט עמודה מסוימת, נראה כמה ערכים שאינם NULL קיימים בעמודה.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נראה לדוגמה כמה ערכים של תקציבי סרטים קיימים במסד הנתונים.
\n", + " נשתמש בפונקציה COUNT על העמודה budget בטבלת movies:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT COUNT(budget)\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי להבין כמה ערכים הם NULL בעמודה הזו, נוכל לבצע חיסור פשוט.
\n", + " נחסר בין כמות הערכים בטבלה לבין כמות הערכים שאינם NULL:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT COUNT(*) - COUNT(budget)\n", + "FROM movies;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כמו שראינו, פונקציות אגרגטיביות פועלות על כל השורות הקיימות, ומחזירות לנו תוצאה אחת.
\n", + " אפשר לדמיין את הפעולה שלהן כמעין מעיכה של כל השורות כדי להשיג תוצאה יחידה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: אם הרחקתי ראות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " האם הגבהים במסד הנתונים שלנו אמינים?
\n", + " אם כן, הרי שממוצע הגבהים של אנשים עד גיל 18 יהיה נמוך מהממוצע של שאר האנשים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו שאילתה שתציג את ממוצע הגבהים של אנשים עד גיל 18, ושאילתה נוספת שתציג את ממוצע הגבהים של אנשים מגיל 18 ומעלה.
\n", + " האם ממוצע הגבהים במסד הנתונים אמין?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## הפסוקית GROUP BY" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כשנבצע מחקר על הנתונים, פעמים רבות נרצה לשאול שאלות על קבוצות של נתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### דוגמה – משקלי פירות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נראה דוגמה חיה על רשימת נתונים קטנה.
\n", + " לפנינו טבלה שבה פירות ומשקלם:
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
דוגמה לטבלת פירות
מספר סידור (id)סוג הפרי (fruit_type)משקל (weight)
1תפוח150
2מנגו540
3תפוח170
4תפוח140
5מנגו460
6תות25
7תפוח150
8תות15
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם נרצה לקבל מה משקל התותים שקטפנו, נוכל לעשות את זה יחסית בקלות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT SUM(weight) AS strawberries_weight\n", + "FROM fruits -- שם הטבלה\n", + "WHERE fruit_type = 'תות';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " התוצאה תהיה עמודה בשם strawberries_weight שבה יש ערך אחד – 40, הרי הוא סכום משקלי התותים בטבלה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בטבלת הדוגמה למעלה אפשר לראות 3 סוגי פירות: תפוח, מנגו ותות.
\n", + " מה אם נרצה לקבל את הסכום של משקלי כל אחד מסוגי הפירות?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בכלים שיש לנו כרגע, אין לנו דרך לכתוב שאילתה שכזו.
\n", + " נצטרך לעבור על כל אחד מסוגי הפירות וליצור עבורם שאילתה דומה לזו שעשינו עבור התות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לו היינו רוצים לפתור את הבעיה באופן ידני, היינו ניגשים אליה כך:\n", + "

\n", + "\n", + "
    \n", + "
  1. נחליט על פי איזו תכונה (עמודה) אנחנו מחלקים את הערכים לקבוצות – במקרה שלנו סוג הפרי.
  2. \n", + "
  3. נחליט מה מעניין אותנו לחשב – איזו תכונה מעניינת אותנו, ואיזו פעולה נרצה לעשות עליה. לדוגמה: סכום, על עמודת המשקל.
  4. \n", + "
  5. נבצע את הפעולה על כל אחת מהקבוצות בנפרד.
  6. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נקבץ את הפירות בטבלה לפי סוג הפרי:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
דוגמה לטבלת פירות, מקובצת לפי סוג הפרי
מספר סידור (id)סוג הפרי (fruit_type)משקל (weight)
1תפוח150
2מנגו540
3תפוח170
4תפוח140
5מנגו460
6תות25
7תפוח150
8תות15
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " תוכלו לדמיין ששמנו את כל אחד מהפירות בדלי שמתאים לסוג שלו.
\n", + " כעת ברשותינו דלי תותים, דלי מנגו ודלי תפוחים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נחליט שמעניין אותנו לחשב את סכום (SUM) המשקלים (weight).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי להגיע לתוצאה, נפעיל את הפונקציה (SUM) על כל אחת מהקבוצות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
משקל לפי סוג פרי
סוג הפרי (fruit_type)סכום המשקלים (weights_sum)
תפוח610
מנגו1000
תות40
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הכל טוב ויפה, אבל איך ניגש לבעיה ב־SQL?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למזלנו, הפסוקית GROUP BY קיימת בדיוק בשביל המטרה הזו.
\n", + " בשאילתה, הפסוקית הזו תאפשר לנו לקבץ ערכים לפי עמודה מסוימת.
\n", + " זה יראה כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "GROUP BY fruit_type\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " זה למעשה החלק בשאילתה ש\"צובע לנו את הטבלה\" בצורה שראינו למעלה.
\n", + " ה\"צביעה\" או \"החלוקה לדליים\" שראיתם למעלה – זה בדיוק מה שהפסוקית הזו עושה בפועל.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נעבור לשאילתה המלאה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " זכרו את השלבים:\n", + "

\n", + "\n", + "
    \n", + "
  1. מחליטים על העמודה שמחלקת לקבוצות – fruit_type.
  2. \n", + "
  3. מחליטים על מה רוצים לעשות עם הקבוצות שנוצרו – סכום (SUM) על המשקלים (weights).
  4. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT fruit_type, SUM(weights) AS weights_sum\n", + "FROM fruits\n", + "GROUP BY fruit_type;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " התוצאה תהיה 3 שורות של סכום המשקלים של כל סוג פרי.
\n", + " דוגמה לתוצאה כזו היא הטבלה הצבועה האחרונה שראיתם.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " חשוב להבין שכשכתבנו GROUP BY בשאילתה שלנו, שינינו לחלוטין את הצורה בה השאילתה עובדת.
\n", + " במקום ש־SELECT תפעל על כל אחת מהשורות,
\n", + " היא עוברת למצב שבו היא פועלת על קבוצות – אותן קבוצות צבעוניות שראינו למעלה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " פסוקית ה־GROUP BY יוצרת את אותן קבוצות,
\n", + " ואז פסוקית ה־SELECT עוברת על קבוצה אחת בכל פעם (במקום על שורה אחת בכל פעם), ומחזירה עבור כל קבוצה תוצאה אחת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### כמות הסרטים שיצאו בכל שנה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " באותו אופן שבו היה לנו קל לענות על משקל התותים בטבלה שלנו,
\n", + " יהיה לנו קל לענות על השאלה \"כמה סרטים יצאו בשנת 1990\":\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT COUNT(*)\n", + "FROM movies\n", + "WHERE \"year\" = 1990;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אבל האם תוכלו לכתוב שאילתה שתחזיר את כמות הסרטים שיצאו בכל שנה שקיימת במסד הנתונים?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " נסו לכתוב שאילתה שתציג כמה סרטים יצאו בכל שנה.
\n", + " בתוצאות השאילתה שתכתבו אמורה להיות רשומה עבור כל אחת מהשנים שקיימות במסד הנתונים.\n", + "

\n", + "
\n", + "
\n", + "

\n", + " חשוב!
\n", + " פתרו לפני שתמשיכו!\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נוכל לשאול את אותה שאלה בקלות גם עבור 1991 ו־1992.
\n", + " למרות שזה בהחלט אפשרי לעבור שנה שנה, זו עשויה להיות משימה מעט מעייפת.
\n", + " אילו רק הייתה דרך לקבץ את הסרטים לפי השנים, ואז לספור כמה סרטים יש בכל קבוצה...\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נכתוב את השאילתה בעזרת פסוקית ה־GROUP BY, כמו שלמדנו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT \"year\", COUNT(*) AS movie_count\n", + "FROM movies\n", + "GROUP BY \"year\";\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשאילתה הזו ביקשנו:\n", + "

\n", + "\n", + "
    \n", + "
  1. חלק את השורות לקבוצות לפי הערכים בעמודת year.
  2. \n", + "
  3. עבור כל קבוצה שכזו, שמורכבת מכל השורות שבהן מופיעה שנה מסוימת, בדוק כמה איברים היא כוללת.
  4. \n", + "
  5. החזר את השנה ואת כמות האיברים שקובצו על פי אותה שנה.
  6. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " המציאו נתונים משל עצמכם, וחישבו על מקומות שבהם הייתם יכולים לנצל את הכוח של פסוקית GROUP BY.
\n", + " נסו לחשוב לפחות על 4 דוגמאות.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הנה הפתרון שלנו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
דוגמאות לשאלות חקר שאפשר לענות עליהן באמצעות חישוב על קבוצה
תיאור הנתוניםשאלת החקרהשדה שלפיו נחלק לקבוצותהפונקציה האגרגטיביתהשדה שעליו נפעיל חישוב
טבלת פירות בה ישות היא סוג הפרי, משקלו ותמונה שלו.מה המשקל הממוצע של כל סוג פרי שיש לנו במסד הנתונים?סוג הפרי (תפוח, אשכולית, בננה)ממוצע (AVG)משקל
טבלת ספרים בה ישות היא שמם, הסוגה (הז'אנר) שלהם ומספר הדפים בכל ספר.מהו הספר בעל מספר העמודים המירבי מכל סוגה (ז'אנר)?הסוגה של הספרמקסימום (MAX)מספר העמודים
טבלת עובדים בה ישות היא שמם, המשכורת שלהם והמחלקה בה הם עובדים.כמה משכורות אנחנו משלמים עבור מחלקה בעסק שלנו?מחלקת העובדסכום (SUM)שכר
טבלת סרטים בה ישות היא שם הסרט, תאריך שחרורו לאקרנים, ציון ממוצע לסרט ומספר מדרגים.עבור כל שנה, מה הציון הממוצע של הסרטים ששוחררו בה?שנת שחרורממוצע (AVG)ציון
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסבך את השאילתה, הפעם רק במעט.
\n", + " נבקש לקבל רק שנים שבהן נכנס הפסקול לסרטים (1927 והלאה).
\n", + " נסדר אותם לפי כמות הסרטים בסדר יורד, ונבקש לקבל רק עד 5 תוצאות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT \"year\", COUNT(*) AS movie_count\n", + "FROM movies\n", + "WHERE \"year\" >= 1927\n", + "GROUP BY \"year\"\n", + "ORDER BY movie_count DESC\n", + "LIMIT 5;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מהשאילתה למעלה אנחנו למדים שפסוקית GROUP BY מגיעה אחרי WHERE ולפני ORDER BY.
\n", + " אבל מהו הסדר לפיו מנוע ה־SQL מריץ את הפסוקיות בשאילתה?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### סדר הרצה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עכשיו, כשאנחנו יודעים איך נראית שאילתה שבנויה מכל הפסוקיות שלמדנו,
\n", + " נרענן את סדר הרצת הפסוקיות במנוע ה־SQL:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בתמונה:\n", + "
\n", + " איור שממחיש את סדר הרצת הפסוקיות בשאילתת SQL שמבצע מנוע ה־SQL על מסד הנתונים.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כמו שאפשר לראות באיור שלמעלה, פסוקית ה־GROUP BY נקראת על ידי מסד הנתונים מייד לפני קריאת פסוקית ה־SELECT.
\n", + " הסדר הזה נקבע כדי לאפשר לפסוקית ה־SELECT להפעיל פונקציות אגרגטיביות על הקבוצות שנוצרו על ידי פסוקית ה־GROUP BY.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: לזכור את הטיטאנים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " רבים טוענים שיש לנו נטייה לייפות את העבר, ולכן דירוגם הממוצע של סרטים ישנים גבוה יותר.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בדקו את הטענה.
\n", + " כתבו שאילתה שתוצאתה היא הדירוג הממוצע של סרטים לפי שנה.
\n", + " כיוון שיש יחסית מעט דירוגים עבור סרטים ישנים מאוד, כללו רק סרטים שיצאו בשנת 1924 או אחריה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עבור כל שנה, עגלו את הציון הממוצע שמצאתם ל־2 ספרות אחרי הנקודה.
\n", + " סדרו את תוצאות השאילתה לפי הדירוג הממוצע באותה שנה כך שהדירוג הגבוה ביותר יופיע ראשון.
\n", + " אם יש 2 דירוגים זהים, הציגו קודם את השנה המוקדמת מביניהן. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הציצו על התוצאות, ושערו בעצמכם אם אפשר להגיד שסרטים ישנים יותר מדורגים גבוה יותר.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### המגבלה של פונקציות אגרגטיביות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ישנה בעיה נפוצה למדי עם GROUP BY ועם פונקציות אגרגטיביות, שתקפוץ עליכם די מהר אחרי שתשתמשו בהן מספיק.
\n", + " חשוב להבין למה היא מתרחשת וכיצד אפשר להתמודד איתה.
\n", + " נסו לחשוב בעצמכם על מה עלול להשתבש בשאילתה הבאה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT \"year\", COUNT(*) AS movie_count, title\n", + "FROM movies\n", + "GROUP BY \"year\";\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " השאילתה האחרונה ביקשה לקבץ את הסרטים לפי שנה, ועבור כל שנה – להחזיר:\n", + "

\n", + "\n", + "
    \n", + "
  1. את השנה עצמה.
  2. \n", + "
  3. את כמות הסרטים ששוחררו באותה שנה.
  4. \n", + "
  5. את כותרת הסרט(?)
  6. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במצב האגרגטיבי שבו אנחנו נמצאים, COUNT הולכת למעוך מספר ישויות לכדי שורה אחת ויחידה.
\n", + " במקרה הזה, אין משמעות לאחזור \"כותרת הסרט\" – אנחנו פועלים על קבוצת סרטים, מספר רשומות.
\n", + " אין סרט אחד שאפשר להתייחס אליו ולשלוף את הכותרת שלו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"אזהרה!\" \n", + "
\n", + "
\n", + "

\n", + " SQLite רחמן ומחזיר תשובה חסרת פשר.
\n", + " רוב המנועים של מסדי הנתונים האחרים יתריעו על חריגה ויכשילו את השאילתה.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לכן, כלל האצבע הוא כזה:
\n", + " בשאילתה שבה יש פסוקית GROUP BY, העמודות בפסוקית ה־SELECT חייבות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### GROUP BY על יותר מעמודה אחת" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הנה תרגיל מעט מאתגר, שתוכלו לנסות לפתור לפני שתמשיכו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עבור כל שנה, הציגו את כל הדירוגים שסרטים קיבלו באותה שנה.
\n", + " ליד כל דירוג, כמה סרטים דורגו כך באותה שנה.
\n", + " כלומר, כל שורה בתוצאת השאילתה צריכה להיות מורכבת מ:\n", + "

\n", + "\n", + "\n", + "\n", + "

\n", + " אם לא היה אף סרט שקיבל דירוג מסוים בשנה מסוימת, מהשאילתה לא תחזור ישות שמכילה את השנה והדירוג הללו.
\n", + " השתמשו ביצירתיות שלכם – הפתרון פשוט, אבל לא למדנו את הטכניקה הזו והיא דורשת מעט אינטואיציה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כך נראות 21 השורות הראשונות שחזרו מפתרון התרגיל:\n", + "

" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "year|avg_vote|times_rating_appeared|\n", + "----|--------|---------------------|\n", + "1894|5.9 | 1|\n", + "1906|6.1 | 1|\n", + "1911|5.7 | 1|\n", + "1911|5.8 | 1|\n", + "1911|6.0 | 1|\n", + "1911|6.2 | 1|\n", + "1911|7.0 | 1|\n", + "1912|5.2 | 1|\n", + "1912|5.5 | 1|\n", + "1912|5.7 | 1|\n", + "1912|6.7 | 1|\n", + "1912|6.8 | 1|\n", + "1913|5.0 | 1|\n", + "1913|6.0 | 1|\n", + "1913|6.2 | 2|\n", + "1913|6.4 | 1|\n", + "1913|6.5 | 1|\n", + "1913|6.6 | 2|\n", + "1913|7.0 | 3|\n", + "1913|7.1 | 1|\n", + "1913|7.5 | 1|" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " השאלה למעלה מלמדת אותנו על היכולת של GROUP BY לעבוד על כמה עמודות יחד.
\n", + " הפעם, הקבוצות שאנחנו רוצים ליצור הן לא לפי עמודה אחת (רק שנה, או רק דירוג).
\n", + " כל קבוצה שניצור תורכב משילוב של כמה עמודות. במקרה שלנו – גם השנה, וגם הדירוג.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עם קצת יצירתיות והכלים שלמדתם, נפריד את השדות ב־GROUP BY בפסיק כדי להשיג את התוצאה:
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT \"year\", avg_vote, COUNT(*) AS times_rating_appeared\n", + "FROM movies\n", + "GROUP BY \"year\", avg_vote\n", + "ORDER BY \"year\", avg_vote;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שימו לב שהשאילתה לא יוצרת קבוצה אחת ל־year וקבוצה אחת ל־avg_vote.
\n", + " היא יוצרת קבוצה לכל שילוב קיים בין year לבין avg_vote.
\n", + " תוכלו לדמיין שנוצר tuple שמורכב מכל העמודות ששמם צוין אחרי פסוקית ה־GROUP BY.
\n", + " הקבוצות חולקו לפי ה־tuple הזה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### GROUP BY לפי ביטוי" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " תעלול נחמד שלא מובן מאליו שיעבוד, הוא חלוקה לקבוצות לפי ביטוי שיצרנו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בתור התחלה, נספור כמה סרטים קיימים במסד הנתונים עבור כל דירוג אפשרי:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT avg_vote, COUNT(*)\n", + "FROM movies\n", + "GROUP BY avg_vote;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " השאילתה למעלה החזירה את כל הדירוגים האפשריים, וכמה סרטים תואמים לכל אחד מאותם דירוגים.
\n", + " הייתי משתף איתכם את תוצאות השאילתה, אבל חזרו 89 שורות, מ־1.0 ועד 9.9. ארוך ומשעמם!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי לקבל מידע שיהיה לנו יותר קל לעכל, בואו נעגל את הדירוג של כל סרט למספר שלם.
\n", + " כך במקום שהדירוג של כל סרט ינוע בין 1.0 ל־9.9 (89 אפשרויות), הוא יהיה בטווח שבין 1 ל־10 (10 אפשרויות).
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " החדשות המשמחות הן שאנחנו יכולים לקבץ את התוצאות לפי הערך המעוגל!
\n", + " נעשה זאת כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT round(avg_vote) AS rating,\n", + " COUNT(*) AS rated_movies\n", + "FROM movies\n", + "GROUP BY round(avg_vote);\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " השאילתה הזו החזירה לנו 10 שורות כשבכל שורה מופיע דירוג מ־1 עד 10.
\n", + " לצד הדירוג בכל אחת מהשורות, מופיעה כמות הסרטים שהדירוג המעוגל שלהם תואם לדירוג שמופיע באותה שורה. \n", + "

" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "rating|rated_movies|\n", + "------|------------|\n", + " 1.0| 107|\n", + " 2.0| 885|\n", + " 3.0| 3039|\n", + " 4.0| 7025|\n", + " 5.0| 14866|\n", + " 6.0| 28367|\n", + " 7.0| 25894|\n", + " 8.0| 5308|\n", + " 9.0| 354|\n", + " 10.0| 10|" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי להגיע לתוצאה הזו, יצרנו סוג של עמודה מחושבת בפסוקית ה־GROUP BY.
\n", + " כמו שאתם רואים, אנחנו יכולים להשתמש בביטוי מפסוקית ה־GROUP BY, בתוך פסוקית ה־SELECT. איזו הקלה!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: חוק בנפורד" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " חוק בנפורד, בהגדרה גסה, הוא חוק שמנבא את שכיחות הספרה השמאלית ביותר במספר, בהינתן אוסף נתונים גדול מספיק.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לפי החוק, ככל שספרה בטווח 1–9 היא נמוכה יותר, יש לה יותר סיכוי להופיע בתור הספרה השמאלית ביותר במספר כלשהו.
\n", + " כלומר, הספרה 1 תופיע בתדירות הגבוהה ביותר כספרה השמאלית במספרים.
\n", + " לפי אותו חוק, הספרה 2 תופיע כספרה השמאלית ביותר במספרים בתדירות נמוכה משל 1, אך גבוהה משל 3.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בדקו את הטענה.
\n", + " כתבו שאילתה שמתייחסת למספר הפעמים שדירגו כל סרט (עמודת votes בטבלת movies).
\n", + " הציגו כמה פעמים כמות הדירוגים מתחילה בספרה 1, כמה פעמים כמות הדירוגים מתחילה בספרה 2 וכך הלאה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הספרה השמאלית ביותר במספר 1,234, לדוגמה, היא 1. הספרה השמאלית ביותר במספר 5 היא 5.
\n", + " ודאו שהספרה 1 היא הספרה השמאלית ביותר של 33834 שורות של כמויות דירוג.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## הפסוקית HAVING" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשאילתות שבנינו עד כה, ראינו את הכוח האדיר של הפסוקיות השונות – מ־SELECT ועד GROUP BY.
\n", + " אחת הפסוקיות השימושיות ביותר הייתה WHERE, שאיפשרה לנו לסנן מהטבלה שורות שלא התאימו לצרכים שלנו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם נציץ בסדר ההרצה של הפסוקיות באיור הצבעוני שמופיע מעלה,
\n", + " נגלה שפסוקית ה־WHERE מורצת לפני פסוקית ה־GROUP BY.
\n", + " מצליחים לזהות אילו בעיות הסדר הזה עלול להביא איתו?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " פסוקית ה־WHERE תעבוד מצוין כשנרצה לסנן שורות לפני שאנחנו מקבצים אותן,
\n", + " אבל לא תוכל לעזור לנו בסינון התוצאות שהוחזרו אחרי הקיבוץ והאגרגציה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נראה דוגמה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נניח שנרצה לספור כמה סרטים טובים יצאו בכל שנה.
\n", + " נגדיר \"סרט טוב\" כסרט שקיבל ציון ממוצע של יותר מ־8.0.
\n", + " סדר הפעולות במקרה הזה הוא:\n", + "

\n", + "\n", + "
    \n", + "
  1. מתוך טבלת הסרטים – FROM movies.
  2. \n", + "
  3. עבור הסרטים שדירוגם הוא יותר מ־8.0 – WHERE avg_vote > 8.0.
  4. \n", + "
  5. קבץ לפי שנת הסרט – GROUP BY \"year\".
  6. \n", + "
  7. והצג את כמות הסרטים בכל קבוצה, כלומר בכל שנה, ליד השנה עצמה – SELECT \"year\", COUNT(*).
  8. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT \"year\", COUNT(*)\n", + "FROM movies\n", + "WHERE avg_vote > 8.0\n", + "GROUP BY \"year\";\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עד כאן עסקים כרגיל, והכל עובד כפי שציפינו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אבל מה קורה, נניח, אם נרצה לקבל רק שנים שבהן יש לפחות 10 סרטים טובים?
\n", + " אם נתאמץ מאוד, נגיע לשאילתה השגויה הבאה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "-- !זהירות, זו שאילתה שלא תעבוד\n", + "SELECT \"year\", COUNT(*)\n", + "FROM movies\n", + "WHERE avg_vote > 8.0\n", + " AND COUNT(*) >= 10\n", + "GROUP BY \"year\";\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כמובן שזה לא יעבוד!
\n", + " פסוקית ה־WHERE רצה לפני GROUP BY ולפני SELECT,
\n", + " כך שכשמנוע ה־SQL מריץ את פסוקית ה־WHERE הוא כלל לא יודע שקיבצנו את הטבלה לקבוצות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי להתגבר על הבעיה הזו, יצרו ב־SQL את פסוקית ה־HAVING.
\n", + " היא פועלת בדיוק כמו פסוקית ה־WHERE, רק שהיא מסננת תוצאות אחרי ה־GROUP BY.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נתקן את השאילתה התקולה.
\n", + " נעביר את תנאי הסינון שנסמך על ה־GROUP BY לפסוקית ה־HAVING:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT \"year\", COUNT(*)\n", + "FROM movies\n", + "WHERE avg_vote > 8.0\n", + "GROUP BY \"year\"\n", + "HAVING COUNT(*) >= 10;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אפשר לסכם ולהגיד:
\n", + " פסוקית ה־HAVING מאפשרת לנו להוסיף לשאילתה סינון לפי פונקציות אגרגטיביות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " גם מבחינת מיקום בשאילתה וגם מבחינת סדר הרצה,
\n", + " פסוקית ה־HAVING תבוא מייד אחרי פסוקית ה־GROUP BY:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " איור שממחיש את סדר הרצת הפסוקיות בשאילתת SQL שמבצע מנוע ה־SQL על מסד הנתונים.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## תרגילים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ילדים זה שמחה?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הוכיחו או הפריכו: קצב הילודה בעולם צונח בקצב מסחרר.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו שאילתה שעבור כל עשור מ־1920, מחשבת את ממוצע הילדים של בעלי התפקידים שנולדו באותו עשור.
\n", + " אל תציגו עשורים שבהם יש לנו נתונים על פחות מ־6,000 בעלי תפקידים.
\n", + " סדרו את הנתונים בסדר יורד, לפי ממוצע הילודה באותו עשור.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### פרס טומי" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " פרס טומי הוא פרס מרגש שהמצאתי ממש הרגע, ומחולק לפי הכללים שמופיעים להלן.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " המועמדים לפרס הם כל הסרטים שדורגו לפחות 30,000 פעמים, והציון הממוצע שלהם הוא לפחות 8.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בפרס טומי, \"אורך הסרט\" מוגדר לפי אורכו של הסרט בשעות.
\n", + " אם אורכו בשעות הוא לא מספר שלם, מעגלים אותו לשלם הקרוב.
\n", + " כך סרט שאורך 61 דקות נחשב כסרט של שעה, וסרט של 100 דקות נחשב כסרט של שעתיים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בכל שנה, ישנה קטגוריה עבור כל אורך סרט אפשרי. בכל קטגוריה מחולק פרס אחד בלבד.
\n", + " הפרס מחולק עבור הסרט שהדירוג שלו הוא הגבוה ביותר עבור אותה שנה, בקטגוריית האורך שלו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כך לדוגמה אם יצאו בשנת 2001 ששת הסרטים הבאים:\n", + "

" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + ".-----.--------.--------.-----.\n", + "|movie|duration|avg_vote|votes|\n", + "|-----+--------+--------+-----|\n", + "| A| 1| 8.1|30001|\n", + "| B| 1| 8.9|16432|\n", + "| C| 1| 8.6|66666|\n", + "| D| 2| 8.2| 2|\n", + "| E| 2| 7.9|72345|\n", + "| F| 3| 9|99999|\n", + "'-----------------------------'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " התוצאות יהיו ש־C זכה בשנת 2001 בקטגוריית האורך של שעה.
\n", + " ל־B לא היו מספיק הצבעות כדי להיות מועמד, ו־A דורג נמוך מ־C ולכן הפסיד לו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בקטגוריית האורך של שעתיים באותה שנה לא זוכה אף סרט.
\n", + " הסרט D נפסל כיוון שאין מספיק אנשים שהצביעו לו, והסרט E נפסל כיוון שהדירוג שלו נמוך מדי.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בקטגוריית האורך של 3 שעות זוכה הסרט F, שהוא גם המתמודד היחיד על הפרס לקטגוריה הזו בשנה הזו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הציגו את הזוכים מכל הזמנים, בכל הקטגוריות.\n", + "

" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week13/4_Normalization.ipynb b/week13/4_Normalization.ipynb new file mode 100644 index 0000000..92d91f6 --- /dev/null +++ b/week13/4_Normalization.ipynb @@ -0,0 +1,2560 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"לוגו" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# נרמול מסדי נתונים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשיעורים האחרונים עבדנו עם מסדי נתונים טבלאיים – כאלו שבנויים מטבלאות שבהן כל שורה מייצגת ישות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם חקרתם קצת את מסד הנתונים והסתכלתם על הנתונים שנמצאים בטבלאות השונות,
\n", + " ייתכן שעלו לכם כמה שאלות מעניינות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כבר בפרק הקרוב נענה על כל השאלות הללו.
\n", + " עד סוף הפרק תבינו מדוע מסדי נתונים בנויים מטבלאות רבות, באופן שלפעמים נראה סבוך,
\n", + " תדעו לארגן מסד נתונים משל עצמכם,
\n", + " ותלמדו מספר מונחים חשובים שישמשו אתכם בהמשך הלמידה על SQL.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לפני שנצא לדרך, נזכיר את הדיאגרמה שהצגנו במחברת הראשונה, כשרק למדנו על השורות והעמודות שבמסד הנתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בתמונה\n", + "
\n", + " תרשים שמציג את כל הטבלאות והעמודות במסד הנתונים, כולל את הקשרים ביניהם.
\n", + " המינוח המקצועי לתרשים שכזה הוא \"תרשים ישויות קשרים\" (באנגלית: ERD).\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## נרמול של מסד הנתונים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במחברת הקרובה אנחנו הולכים לעבור ביחד מסע של יצירת מסד נתונים קטן שכולל סרטים ושחקנים.
\n", + " תוך כדי המסע, נבחן החלטות ורעיונות שקשורים בתכנון ובבנייה של מסד הנתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לפני שנתחיל, נציב לעצמנו מטרות.
\n", + " בבואנו לתכנן מסד נתונים, היינו רוצים לדעת מה עלינו לעשות כדי לענות על הצרכים הבאים:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " יצירה או שינוי של מסד הנתונים כך שיתחשב בנקודות הללו, הוא תהליך שנקרא \"נרמול של מסד הנתונים\" (Database normalization).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נתחיל!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### טבלה אחודה של סרטים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כל הטבלאות האלו הן בלאגן אחד גדול. אנחנו נלך על משהו פשוט יותר.
\n", + " כצעד ראשון, נבנה טבלה פשוטה של סרטים.
\n", + " העמודות בה יהיו רק כותרת הסרט והשנה שבה הוא יצא.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת movies
titlerelease_year
Monty Python and the Holy Grail1975
V for Vendetta2005
The Matrix1999
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נוסיף את השחקנים שכיכבו בכל סרט ישירות לטבלת הסרטים שלנו.
\n", + " כך נוכל לחסוך את הסיבוך שבהוספת טבלת השחקנים.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת movies
titlerelease_yearactor_name
Monty Python and the Holy Grail1975Graham Chapman, John Cleese, Eric Idle, Terry Gilliam
V for Vendetta2005Hugo Weaving, Rupert Graves, Stephen Rea
The Matrix1999Keanu Reeves, Laurence Fishburne, Hugo Weaving
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במבט ראשון – זה פנטסטי!
\n", + " יצרנו טבלה שכוללת את שמות השחקנים בכל סרט, בלי להסתבך עם יצירת טבלה נוספת.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נצייר ERD של הטבלה החדשה שלנו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " ה־ERD של מסד הנתונים שלנו.
\n", + " כרגע הוא כולל רק את הטבלה movies, את עמודותיה ואת סוגי הנתונים שבהם.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " אם נקדיש עוד רגע להתבונן בישויות שיצרנו, נוכל למצוא כמה וכמה בעיות שהסידור הזה יוצר.
\n", + " יכולים לחשוב על כמה מהן?\n", + "

\n", + "
\n", + "
\n", + "

\n", + " חשוב!
\n", + " פתרו לפני שתמשיכו!\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### אי־פריקות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הנה כמה בעיות מרכזיות שנוצרות בעקבות הצורה בה בחרנו לבנות את הטבלה:\n", + "

\n", + "\n", + "
    \n", + "
  1. כששמו של שחקן ישתנה, נצטרך לעדכן את שמו בכל השורות בהן מופיע שם השחקן. זה יהיה מסורבל, ועלול לגרום לטעויות.
  2. \n", + "
  3. הוספת מידע על אחד השחקנים, כמו תאריך יום הולדתו, תהיה מסורבלת ותיצור שאילתות מסורבלות בעתיד.
  4. \n", + "
  5. השאילתה שמאחזרת סרטים בהם שיחק שחקן מסוים עשויה להיות מסורבלת ולכלול מקרי קצה של חיפושים במחרוזות.
  6. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לכן, אחת מהפעולות החשובות בנרמול של מסד נתונים הוא לוודא את האי־פריקות (atomicity) של הנתונים בו.
\n", + " במילים פשוטות: שבכל תא בכל אחת מהטבלאות יש נתון שאי־אפשר לפרק ליחידות קטנות יותר.
\n", + " במקרה שלנו, כל תא בעמודה actor_name בנוי משמותיהם של כמה שחקנים, וזוהי הפרה של רעיון האי־פריקות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כיצד נתקן את המצב?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אחת הרעיונות המיידיים שאולי קפצו לכם לראש הוא ליצור עמודה עבור כל אחד מהשחקנים.
\n", + " התיקון הזה בעייתי, כיוון שהוא משאיר אותנו עם טבלה שקשה לתשאל.
\n", + " נסו לדמיין את הסרבול שבכתיבת שאילתה שבודקת אם שחקן מסוים שיחק במטריקס כשיש לנו 20 עמודות של שחקנים.
\n", + " בנוסף, הרעיון הזה עדיין לא מאפשר לנו להוסיף מידע בקלות על כל אחד מהשחקנים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אפשרות אחרת שתפתור עבורנו את כל הבעיות שהצגנו היא ליצור שורה עבור כל שחקן.
\n", + " כך המידע יענה על דרישת האי־פריקות, ויאפשר לנו להוסיף בקלות מידע על אודות השחקנים שהשתתפו בסרט.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נוסיף עבור כל שחקן שורה משלו, ועל הדרך ניצור עמודה שבה יופיע תאריך לידתו של כל שחקן.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת movies
titlerelease_yearactor_nameactor_birth_date
Monty Python and the Holy Grail1975Graham Chapman1941-01-08
Monty Python and the Holy Grail1975John Cleese1939-10-27
Monty Python and the Holy Grail1975Eric Idle1943-03-29
Monty Python and the Holy Grail1975Terry Gilliam1940-11-22
V for Vendetta2005Hugo Weaving1960-04-04
V for Vendetta2005Rupert Graves1963-06-30
V for Vendetta2005Stephen Rea1946-10-31
The Matrix1999Keanu Reeves1964-09-02
The Matrix1999Laurence Fishburne1961-07-30
The Matrix1999Hugo Weaving1960-04-04
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הצלחה!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נעדכן את ה־ERD:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " ה־ERD של מסד הנתונים שלנו לאחר שהוספנו עמודה.
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אבל הנודניקים שבינינו שוב מעקמים את האף.
\n", + " \"גם בצורה הזו יש לא מעט בעיות!\", הם נזעקים.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " קחו רגע ונסו לחשוב מה הבעיות שעולות מהעיצוב החדש שיצרנו עבור מסד הנתונים.\n", + "

\n", + "
\n", + "
\n", + "

\n", + " חשוב!
\n", + " פתרו לפני שתמשיכו!\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: מן המסד" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בנו טבלה שמכילה פרטים על משתמשים בקורס פייתון.
\n", + " הטבלה תכלול את שם המשתמש, תאריך יום ההולדת שלו (אם הוא סיפק כזה) ואת התרגילים שפתר.
\n", + " עבור כל תרגיל שפתר המשתמש קיימות התכונות הבאות: שם, מספר מחברת ומועד אחרון להגשה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " צרו ERD שמתאר את הטבלה שיצרתם.
\n", + " תוכלו להשתמש ב־vuerd, או בכל כלי אחר שיהיה לכם נוח עבור המשימה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### צימוד וכפילויות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אנחנו בטוחים שהצלחתם לחשוב על לא מעט בעיות בצורה החדשה בה אירגנו את הטבלה.
\n", + " עבורנו, אחת הבעיות הראשונות שזועקות מהטבלה היא כפילות הנתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אנחנו לא סתם נטפלים לכפילות הזו – היא ממש מסוכנת.
\n", + " נתמקד בכפילות של שחקנים – שהרי אותו שחקן יכול לשחק בכמה סרטים.
\n", + " בדוגמה שלנו, השחקן הנפלא הוגו ויבינג משחק גם בסרט The Matrix וגם בסרט V for Vendetta.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם נרצה להוסיף סרט שבו משחק ויבינג, נצטרך לציין שוב את כל פרטי השחקן שלו – שכבר כתובים בשורה אחרת.
\n", + " בדוגמה שלנו זה רק תאריך יום ההולדת, אבל לו היו עמודות נוספות היינו צריכים להעתיק שוב את כולן. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### חריגויות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " זה אפילו לא החלק הגרוע ביותר. מה יקרה כשנרצה לעדכן את כתובת מגוריו של ויבינג, לדוגמה?
\n", + " נצטרך לכתוב שאילתה שתעבור ותשנה בכל השורות בהן הוא מופיע את כתובת המגורים שלו.
\n", + " ומה אם השאילתה תיכשל באמצע, או שהשאילתה שניסחנו לא הצליחה לתפוס את כל השורות בהן ויבינג מופיע?
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במקרה שכזה, כשנשאל את מסד הנתונים איפה ויבינג גר יחזרו לנו שתי כתובות שונות.
\n", + " זו אי־סדירות בנתונים שלנו – מצב שבו יש לנו נתונים סותרים במסד הנתונים, ממנו אנחנו חייבים להימנע בכל דרך.
\n", + " הבעיה הזו, שבה מידע כפול מסכן את עקביות הנתונים, נקראת חֲרִיגוּת עדכון (Update anomaly).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בעיה נוספת עלולה להיווצר כשננסה להוסיף סרט חדש.
\n", + " בסרט האנימציה המוכר Fantasia 2000, לדוגמה, לא ליהקו שחקנים.
\n", + " אם נרצה להוסיף אותו למסד הנתונים, לא ברור מה נכניס בעמודות השמורות למידע על השחקנים.
\n", + " הבעיה הזו, שבה אי אפשר להכניס מידע חדש בגלל חוסר במידע אחר, נקראת חֲרִיגוּת הכנסה (Insertion anomaly).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ומה באשר למחיקת נתונים?
\n", + " אם ישנו שחקן שמשחק רק בסרט אחד, מחיקה של הסרט ממסד הנתונים תמחק לנו את כל המידע שקיים לנו על השחקן.
\n", + " זה אומר שכשנרצה להוסיף את השחקן לסרט אחר בעתיד, יהיו חסרים לנו נתונים כמו יום ההולדת שלו.
\n", + " הבעיה הזו, שבה מחיקה של מידע לא נחוץ גוררת איבוד של מידע נחוץ, נקראת חֲרִיגוּת מחיקה (Deletion anomaly).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### תמיד יכול להיות גרוע יותר" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ויש עוד!
\n", + " אם נרצה להוסיף עבור כל אחד מהשחקנים את שמות הילדים שלו, נצטרך להוסיף שורה עבור כל ילד כדי לשמור על אי־פריקות.
\n", + " ואם נרצה לכלול את תחביביו של כל ילד?\n", + "

\n", + "

\n", + " בצורה הזו כל עמודה שנרצה להוסיף תגדיל לנו את מספר הישויות משמעותית.
\n", + " בסרט שבו יש 5 שחקנים, לכל שחקן יש 3 ילדים ולכל ילד 6 תחביבים,
\n", + " יוצא שעבור $1 + 5 + 3 + 6 = 15$ ישויות נצטרך ליצור $1 \\cdot 5 \\cdot 3 \\cdot 6 = 90$ ישויות במסד הנתונים!
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם אתם זועקים מבעד למסך המחשב \"בסדר, בסדר, נודניק! נפריד לטבלאות!\" זה רק אומר שאתם נורמליים לחלוטין.
\n", + " נפריד לטבלאות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת movies
titlerelease_yearactor_name
Monty Python and the Holy Grail1975Graham Chapman
Monty Python and the Holy Grail1975John Cleese
Monty Python and the Holy Grail1975Eric Idle
Monty Python and the Holy Grail1975Terry Gilliam
V for Vendetta2005Hugo Weaving
V for Vendetta2005Rupert Graves
V for Vendetta2005Stephen Rea
The Matrix1999Keanu Reeves
The Matrix1999Laurence Fishburne
The Matrix1999Hugo Weaving
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת actors
namebirth_date
Graham Chapman1941-01-08
John Cleese1939-10-27
Eric Idle1943-03-29
Terry Gilliam1940-11-22
Hugo Weaving1960-04-04
Rupert Graves1963-06-30
Stephen Rea1946-10-31
Keanu Reeves1964-09-02
Laurence Fishburne1961-07-30
Hugo Weaving1960-04-04
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עכשיו נוכל להעשיר את טבלת actors מבלי לדאוג להשפעות שיהיו לעריכות שלנו על טבלת movies.
\n", + " נוסיף תאריך פטירה וגובה, ונמחק את הכפילות שיש לנו במסד הנתונים עבור השחקן הוגו ויבינג:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת actors
nameheightbirth_datedeath_date
Graham Chapman191.01941-01-081989-10-04
John Cleese196.01939-10-27NULL
Eric Idle186.01943-03-29NULL
Terry Gilliam175.01940-11-22NULL
Hugo Weaving188.01960-04-04NULL
Rupert Graves180.01963-06-30NULL
Stephen Rea179.01946-10-31NULL
Keanu Reeves186.01964-09-02NULL
Laurence Fishburne184.01961-07-30NULL
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מצוין!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נעדכן את ה־ERD:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " ה־ERD של מסד הנתונים שלנו לאחר העדכון, עם טבלת actors והעמודות הנוספות.
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### אילוצים – NULL" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נפנה לרגע תשומת לב לעמודת תאריך הפטירה, שמלאה ב־NULL־ים.
\n", + " בעמודות מסוימות, כמו בעמודת תאריכי הפטירה, אנחנו מצפים לראות NULL בחלק מהתאים.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מתי נראה NULL במסד הנתונים שלנו בדרך כלל?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "
  1. כשומר מקום עבור ערך שעדיין לא קיים – לדוגמה, אם השחקן עדיין חי ואין עבורו תאריך פטירה.
  2. \n", + "
  3. כמחליף של מידע חסר – כמו שחקן שאין לנו נתונים לגבי תאריך יום הולדתו.
  4. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לעומת עמודות שעשויות לכלול NULL, יש עמודות שבהן אנחנו ממש בטוחים שלא יופיע NULL.
\n", + " אחת מהן, לדוגמה, היא שם השחקן. אין משמעות להכנסת שחקן למסד הנתונים אם אנחנו לא יודעים את שמו.
\n", + " נוכל להגיד שאנחנו משוכנעים שהעמודה name לעולם לא תכיל NULL.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " יתרון משמעותי שמסדי נתונים טבלאיים מספקים לנו הוא היכולת לאכוף עקביות בנתונים שאנחנו מאחסנים בו.
\n", + " אפשר להגביל את הערכים שיכולים להיכנס לעמודה מסוימת במסד הנתונים בעזרת הגדרת אילוצים (constraints).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לדוגמה, על העמודה name בטבלה actors, נרצה להוסיף את האילוץ NOT NULL – אילוץ שמוודא שהנתון שהוזן לעמודה אינו NULL.
\n", + " לרוב, מי שבונה את מסד הנתונים הוא זה שמחליט על האילוצים בעמודות השונות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם תחזרו לתרשים שמתאר את מסד הנתונים, זה שמופיע בתחילת הפרק,
\n", + " תוכלו לזהות בקלות עבור אילו עמודות הוגדר האילוץ NOT NULL.
\n", + " לצד העמודות שכן מתירות הזנת NULL, מופיע סימן שאלה, שרומז על כך שלא חייב להיות בעמודה ערך.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נוסיף ל־ERD שלנו סימונים שמסמנים באילו עמודות עשוי להיות NULL:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " ה־ERD של מסד הנתונים שלנו לאחר העדכון, עם סימון העמודות שעשויות להכיל NULL.
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: ועד הטפחות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בהמשך לתרגיל הקודם, הפרידו את הנתונים לטבלאות וסמנו שדות שניתן להזין בהם NULL.
\n", + " הוסיפו עמודה שמכילה מידע על מצב התרגיל, ובה אחד מהערכים \"לא הוגש\", \"הוגש\" או \"נבדק\".\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " צרו ERD שמתאר את הטבלה שיצרתם.
\n", + " תוכלו להשתמש ב־vuerd, או בכל כלי אחר שיהיה לכם נוח עבור המשימה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### מפתח ראשי" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " התגעגעתם למשחק שבו אתם מוצאים את הבעיה במסד הנתונים שיצרנו?
\n", + " מצאו את הבעיות שעשויות להיווצר מארגון הנתונים כפי שעשינו ב־ERD האחרון.\n", + "

\n", + "
\n", + "
\n", + "

\n", + " חשוב!
\n", + " פתרו לפני שתמשיכו!\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הצלחתם לחשוב על משהו?
\n", + " אם תשקיעו מספיק זמן, ודאי תצליחו לחשוב על בעיות רבות.
\n", + " נתמקד באחת מרכזית: הייחודיות של כל ישות בטבלאות שיצרנו.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נניח שבטבלת הסרטים מופיעים שני סרטים – Star Wars ו־Dial M for Murder.
\n", + " בסרט Star Wars משחק שחקן בשם John Williams.
\n", + " תוכלו למצוא אותו במסד הנתונים שמשמש אתכם לתרגול, בטבלת names תחת המזהה nm0002354.
\n", + " בסרט Dial M for Murder של היצ'קוק (תורגם ל\"אליבי\", אם תהיתם) משחק שחקן ששמו הוא... John Williams.
\n", + " המזהה שלו במסד הנתונים הוא nm0002369.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " זו לא טעות!
\n", + " אמנם שמם של שני השחקנים זהה, אך מדובר בשחקנים שונים.
\n", + " כיצד נבדיל ביניהם? הרי בעמודת actor_name שבטבלת movies בשני המקרים יהיה כתוב John Williams.
\n", + " איך נדע לאיזה שחקן התא שבטבלת movies מתייחס?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הבעיה שלנו היא היכולת לזהות שחקן באופן חד־חד ערכי.
\n", + " שם של שחקן או שם של סרט הם נתונים שעשויים להיות בהם כפילויות, ולכן אינם מזהים טובים דיו.
\n", + " אם נרצה להתייחס לסרט \"The Lion King\" – איך נדע האם מדובר בקלאסיקה הנפלאה משנת 1994 או בפשע המודרני משנת 2019?
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אילו היינו מוסיפים לטבלת actors מזהה כלשהו, כמו nm0002354 עבור ג'ון הראשון ו־nm0002369 עבור ג'ון השני,
\n", + " היינו יכולים להתייחס בטבלת movies למזהה הזה במקום לשם השחקן.
\n", + " בצורה הזו היה קל לנו יותר להבין על איזה John Williams מדובר בכל אחד מהסרטים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " המסקנה המתבקשת היא שעבור כל ישות במסד הנתונים, אנחנו חייבים נתון שיאפשר לזהות אותה באופן ייחודי.
\n", + " אם היינו מנהלים מסד נתונים של מרשם האוכלוסין, שבו יש את פרטיהם של כל האזרחים במדינת ישראל.
\n", + " איזו שיטה יש לנו לזהות אדם מסוים בישראל באופן ייחודי?
\n", + " איך נדע להבדיל בין משה כהן אחד למשנהו?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " השיטה שמצאו במשרד הפנים היא שכל אחד מהאנשים יקבל מספר ייחודי לו.
\n", + " אתם ודאי מכירים את המספר הזה בתור \"תעודת זהות\".
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במסדי נתונים נהוג להוסיף לכל טבלה עמודה שמאחסנת נתון ייחודי, כזה שישתנה בין ישות לישות ויזהה אותה באופן חד־חד ערכי.
\n", + " לפי הרעיון, לכל שורה מוקצה מזהה שלעולם לא יהיה NULL ולעולם לא יחזור על עצמו באותה טבלה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " דרך פופולרית לממש מזהה ייחודי היא פשוט מספר רץ (autoincrement):
\n", + " השורה הראשונה שמתווספת לטבלה מקבלת את המזהה 1, השורה השנייה מקבלת את המזהה 2 וכן הלאה.
\n", + " זו דרך מצוינת להימנע ממצב בו אותו מזהה מופיע בשתי ישויות שונות באותה טבלה, ולשמור על דרך לזהות כל ישות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הרעיון הנפלא הזה, של עמודה שמכילה נתון שמאפשר לנו לזהות באופן ייחודי כל ישות, נקרא \"מפתח ראשי\" (primary key).
\n", + " תוכלו לראות את המפתח הראשי בכל אחת מהטבלאות ב־ERD שמופיע בתחילת הפרק –
\n", + " הוא מסומן בעזרת סמליל של מפתח ליד שם העמודה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ניצור מפתחות ראשיים עבור השחקנים והסרטים.
\n", + " במקום לציין את שם השחקן בטבלת הסרטים, נציין את המספר הייחודי לו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת movies
movie_idtitlerelease_yearactor_id
1Monty Python and the Holy Grail1975Graham Chapman 1
2Monty Python and the Holy Grail1975John Cleese 2
3Monty Python and the Holy Grail1975Eric Idle 3
4Monty Python and the Holy Grail1975Terry Gilliam 4
5V for Vendetta2005Hugo Weaving 5
6V for Vendetta2005Rupert Graves 6
7V for Vendetta2005Stephen Rea 7
8The Matrix1999Keanu Reeves 8
9The Matrix1999Laurence Fishburne 9
10The Matrix1999Hugo Weaving 5
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת actors
actor_idnameheightbirth_datedeath_date
1Graham Chapman191.01941-01-081989-10-04
2John Cleese196.01939-10-27NULL
3Eric Idle186.01943-03-29NULL
4Terry Gilliam175.01940-11-22NULL
5Hugo Weaving188.01960-04-04NULL
6Rupert Graves180.01963-06-30NULL
7Stephen Rea179.01946-10-31NULL
8Keanu Reeves186.01964-09-02NULL
9Laurence Fishburne184.01961-07-30NULL
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נעדכן את ה־ERD כך שישקף את השינויים שביצענו בטבלאות.
\n", + " נוסיף עמודות של מפתחות ראשיים לכל אחת מהטבלאות, ונסמן אותן בהתאם.
\n", + " נשנה את העמודה actor_name בטבלת movies כך שתצביע על actor_id שבטבלת actors.
\n", + " נשנה את שם העמודה ל־actor_id, ונגדיר את טיפוס הנתונים שהיא מכילה כ־int במקום כ־text.
\n", + " ברוב ה־ERD־ים אתם תראו מפתח קטן ליד השורה שמייצגת את המפתח הראשי של הטבלה, או את ראשי התיבות \"PK\".\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " ה־ERD של מסד הנתונים שלנו לאחר העדכון, עם סימון המפתחות הראשיים והעדכון לעמודות.
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### מפתח זר" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עבור כל ישות בטבלת movies, עמודת actor_id מצביעה על שחקן שבטבלת actors.
\n", + " לצורך כך היא משתמשת במפתח הראשי שבטבלת actors, עמודה ששמה actor_id, שמקצה מזהה ייחודי לכל שחקן.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הרעיון הזה מאפשר לנו להתייחס לשחקן מטבלה אחרת מבלי לחשוש מבעיה של רב־משמעות לגבי זהות השחקן.
\n", + " עמודה כמו actor_id בתוך טבלת movies נקראת \"מפתח זר\" (foreign key).
\n", + " אנחנו קוראים לעמודה \"מפתח זר\" כשהערכים שבה מצביעים על מפתח ראשי של טבלה אחרת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם נגדיר במסד הנתונים שעמודת actor_id מטבלת movies היא מפתח זר שמצביע לנתונים מעמודת actor_id שבטבלת actors,
\n", + "שימוש במספר שחקן לא קיים (נניח, 10) בעמודת actor_id שבטבלת movies יגרום להתרעה על חריגה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " ה־ERD של מסד הנתונים שלנו לאחר סימון המפתחות הזרים.
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### קשר יחיד לרבים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בצורה הנוכחית של הטבלה, אנחנו יכולים להגיד שמתקיים בין טבלת movies לבין טבלת actors קשר שנקרא \"יחיד לרבים\".
\n", + " זהו מונח שנמצא בשימוש נפוץ. פירושו שבטבלת movies יכולות להיות שורות רבות שיצביעו על שחקן יחיד מטבלת actors.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נחדד: לפי העיצוב הנוכחי, כל שורה בטבלת השחקנים, יכולה להופיע בכמה שורות בטבלת הסרטים.
\n", + " באופן רשמי, נוכל להגיד ש\"כל ישות ב־actors יכולה להופיע בישויות רבות ב־movies\".
\n", + " קשר כזה בין טבלאות נקרא \"קשר יחיד לרבים\" (one-to-many relationship).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " יש שתי דרכים לסמן ב־ERD קשר של יחיד לרבים.
\n", + " בשתיהן, נחבר את שתי העמודות הקשורות בקו.
\n", + " בדרך הראשונה, נכתוב ליד העמודה של ה\"רבים\" את האות N, וליד העמודה של ה\"יחיד\" את הספרה 1.
\n", + " בצורה השנייה, נסמן את הצד של ה\"יחיד\" בשני קווים או בקו שאחריו עיגול. את הצד של ה\"רבים\" נסמן כמשולש פתוח עם קו אופקי שיוצא מקודקודו.
\n", + " כל עוד לא העמקתם בקריאה נוספת באינטרנט על פשר הסימונים, העדיפו לסמן קשרים בדרך הראשונה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " ה־ERD של מסד הנתונים שלנו לאחר סימון קשר יחיד לרבים.
\n", + " בחרנו לסמן את קשר היחיד לרבים בשתי הצורות שהוזכרו.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשביל העניין, נניח שאנחנו רוצים שטבלת הסרטים תכלול עמודה נוספת – השפה העיקרית שבה מדברים בסרט המקורי.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת movies
movie_idtitlerelease_yearactor_idoriginal_language
1Monty Python and the Holy Grail19751English
2Monty Python and the Holy Grail19752English
3Monty Python and the Holy Grail19753English
4Monty Python and the Holy Grail19754English
5V for Vendetta20055English
6V for Vendetta20056English
7V for Vendetta20057English
8The Matrix19998English
9The Matrix19999English
10The Matrix19995English
11Cidade de Deus200210Portuguese
12Cidade de Deus200211Portuguese
13Cidade de Deus200212Portuguese
14Cidade de Deus200213Portuguese
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שימו לב לכמות החזרות של English ו־Portuguese בעמודת original_language.
\n", + " נוכל למנוע את החזרה על המחרוזת אם נוציא את הערכים ב־original_language לטבלה נפרדת,
\n", + " וניצור קשר של יחיד לרבים בין טבלת השפות החדשה לטבלת הסרטים.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ניצור את טבלת השפות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת languages
language_idname
1English
2Portuguese
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ונעדכן את הערכים בעמודה החדשה שיצרנו ב־movies כך שיהיו מפתח זר למפתח הראשי language_id שבטבלת languages:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת movies
movie_idtitlerelease_yearactor_idlanguage_id
1Monty Python and the Holy Grail197511
2Monty Python and the Holy Grail197521
3Monty Python and the Holy Grail197531
4Monty Python and the Holy Grail197541
5V for Vendetta200551
6V for Vendetta200561
7V for Vendetta200571
8The Matrix199981
9The Matrix199991
10The Matrix199951
11Cidade de Deus2002102
12Cidade de Deus2002112
13Cidade de Deus2002122
14Cidade de Deus2002132
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הקשר הוא יחיד לרבים כיוון שכל שפה מטבלת השפות יכולה להופיע פעמים רבות בעמודה language_id שבטבלת movies.
\n", + " נבנה את ה־ERD המתאים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " ה־ERD של מסד הנתונים שלנו לאחר הוספת טבלת languages וסימון קשרי הגומלין.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בהמשך המחברת נזנח את התוספת של השפות לטבלת הסרטים כדי לפשט את הדוגמאות.
\n", + " אם תרצו, תוכלו לדמיין שהשינויים שעשינו קיימים גם בדוגמאות הבאות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### קשר רבים לרבים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " תיאורטית, היינו רוצים שכל שחקן יוכל לשחק בכמה סרטים.
\n", + " הצורה הנוכחית שבה סידרנו את הטבלאות מאפשרת לנו לעשות זאת באלגנטיות, באמצעות הקישור שעשינו בין טבלת actors לטבלת movies.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " באותה צורה, נרצה שבכל סרט נוכל להצביע על כמה וכמה שחקנים.
\n", + " כרגע, כדי להשיג את המטרה הזו, אנחנו משכפלים את שורת הסרט, ובכל שורה שכזו מזינים מצביע לאחד השחקנים בסרט.
\n", + " כבר דנו לעיל בחסרונות המובהקים של שכפול נתונים, ובחריגויות שהחסרונות הללו מביאים איתם.
\n", + " ניגש לעיקר: איך מונעים את הכפילות בטבלת הסרטים, שומרים על האי־פריקות ועדיין מאפשרים כמה שחקנים באותו סרט?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " המחשבה הראשונה שעשויה לעבור לנו בראש היא לאחסן מצביע לסרטים בטבלת השחקנים.
\n", + " הפתרון הזה ייצור לנו בעיה דומה לבעיה שיש לנו כרגע –
\n", + " הוא יכריח אותנו לשכפל את שורת השחקן שבטבלת actors כדי לגרום לו להצביע לכמה סרטים, ומהשכפול הזה אנחנו מנסים להימנע.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אז מה בכל זאת הפתרון?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הדרך הקלאסית לפתור את הבעיה הזו היא ליצור טבלה נוספת, שמייצגת את הקשרים שבין סרטים לבין שחקנים.
\n", + " כל שורה תכיל מספר מזהה של סרט, ומספר מזהה של שחקן.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסו לדמיין את זה ויזואלית:
\n", + " טבלת movies וטבלת actors נמצאות זו לצד זו,
\n", + " והטבלה החדשה שניצור אומרת מאיזו שורה בטבלת movies למתוח קו, ולאיזו שורה בטבלת actors הקו אמור להגיע.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת movies
movie_idtitlerelease_year
1Monty Python and the Holy Grail1975
2V for Vendetta2005
3The Matrix1999
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת movie_actors
movie_idactor_id
11
12
13
14
25
26
27
35
38
39
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת actors
actor_idnameheightbirth_datedeath_date
1Graham Chapman191.01941-01-081989-10-04
2John Cleese196.01939-10-27NULL
3Eric Idle186.01943-03-29NULL
4Terry Gilliam175.01940-11-22NULL
5Hugo Weaving188.01960-04-04NULL
6Rupert Graves180.01963-06-30NULL
7Stephen Rea179.01946-10-31NULL
8Keanu Reeves186.01964-09-02NULL
9Laurence Fishburne184.01961-07-30NULL
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " טבלת movie_actors היא טבלה שמכילה שני מפתחות זרים:
\n", + " אחד שמצביע על המפתח הראשי של טבלת movies, ואחד שמצביע על המפתח הראשי של טבלת actors.
\n", + " הטבלה הזו, שנקראת בעגה המקצועית טבלה מקשרת (junction table), היא מעין טבלת עזר.
\n", + " קיומה מאפשר לנו להגדיר קשר מורכב, בו שחקן יכול להופיע בכמה סרטים, ובכל סרט יכולים לשחק כמה שחקנים.
\n", + " הקשר הזה, שמוגדר בין movies לבין actors נקרא קשר רבים לרבים (many-to-many relationship).\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"\n",\n", + "
\n", + " ה־ERD המעודכן שמשקף את קשר הרבים לרבים שבין טבלת movies לבין טבלת actors.
\n", + " לעיתים, תרשימים ישמיטו את הטבלה המקשרת ויסמנו N בקצוות הקווים.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: ומחוץ עד החצר הגדולה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בהמשך לתרגיל הקודם, הוסיפו לטבלאות שיצרתם מפתחות ראשיים וצרו ביניהם קשרי גומלין.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " צרו ERD שמתאר את הטבלה שיצרתם.
\n", + " תוכלו להשתמש ב־vuerd, או בכל כלי אחר שיהיה לכם נוח עבור המשימה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## חוקי נרמול" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במחברת זו ראינו את התהליך המחשבתי ואת השיקולים הבסיסיים שמלווים מתכנת בבניית מסד נתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " צעד אחר צעד ביצענו תהליך שנקרא נרמול של מסד נתונים.
\n", + " התהליך אמור לוודא שמבנה מסד הנתונים יהיה בנוי באופן מיטבי:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "
  1. שעדכון, מחיקה או הוספת רשומות לא תפגום בשלמות או בתקינות הנתונים במסד הנתונים.
  2. \n", + "
  3. שיקטן הסיכוי שיהיה צורך בארגון מחדש של מסד הנתונים.
  4. \n", + "
  5. שמסד הנתונים יהיה קריא ונוח לשימוש עבור המשתמשים בו.
  6. \n", + "
  7. שאפשר יהיה לבצע שאילתות בקלות על מסד הנתונים, גם לאחר שהוא ישתנה.
  8. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למרות שביצענו את הנרמול בעזרת היגיון בריא וקצת משחקים עם טבלאות,
\n", + " הנושא של נרמול טבלאות נחקר לעומקו ונדון שוב ושוב בספרות אקדמית משמימה.
\n", + " לאורך השנים נוצרו \"חוקי נרמול\", שמטרתם לתת שיטה טכנית ומדורגת לנרמול של מסדי נתונים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסקור את חלקם בקצרה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### צורה נורמלית ראשונה (1NF)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " טבלה היא מצורה נורמלית ראשונה אם אין בה נתונים מיותרים או כפולים.
\n", + " צורת נרמול זו מכריחה את הטבלה לעמוד בדרישות הבאות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### צורה נורמלית שנייה (2NF)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " טבלה היא מצורה נורמלית שנייה אם היא עומדת בצורת הנורמליות הראשונה, וגם כל עמודה בטבלה תלויה בכל עמודות המפתח.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הכלל הזה בעיקרון מתייחס למצבים מורכבים, בהם יש מפתחות ראשיים שמורכבים מ־2 עמודות או יותר (כן, זה אפשרי).
\n", + " תוכלו לראות מצב כזה בטבלת principals ב־ERD.
\n", + " אם המפתח הראשי שלכם מורכב מעמודה אחת בלבד – מזל טוב, הטבלה שלכם היא מצורה נורמלית שנייה.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם בטבלה שלכם יש מפתח ראשי שמורכב מכמה עמודות,
\n", + " הצורה הנורמלית השנייה דורשת שכל אחת מהעמודות שאינן עמודות המפתח, יהיו תלויות בכל עמודות המפתח.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ניקח מצב לדוגמה בו בנינו טבלה שבה יש עמודה למספר הסרט, ועמודה למספר השחקן.
\n", + " הגדרנו שהמפתח הראשי הוא שילוב של שתי העמודות הללו.
\n", + " טבלה שכזו היא ללא כפילויות, כך שטכנית היא עומדת בצורה הנורמלית הראשונה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אבל אז נוסיף עמודות נוספות לטבלה, כמו שם הסרט, אורך הסרט ושם השחקן.
\n", + " אף אחת מהעמודות הללו לא מקיימת את התנאים של הצורה הנורמלית השנייה:
\n", + " העמודות של אורך הסרט ושל שם הסרט אינן תלויות בעמודה של מספר השחקן.
\n", + " בדיוק באותה מידה, העמודה של שם השחקן לא תלויה בעמודה של מספר הסרט.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כיוון שישנן עמודות שלא מתייחסות לכל המפתח הראשי, הטבלה אינה נחשבת מצורה נורמלית שנייה.
\n", + " כדי לתקן את המצב, נצטרך להפריד את הטבלאות ל־2 – לטבלת סרטים ולטבלת שחקנים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### צורה נורמלית שלישית (3NF)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " טבלה היא מצורה נורמלית שלישית אם היא עומדת בצורת הנורמליות השנייה, וגם כל עמודה בטבלה לא תלויה בעמודות שאינן עמודות המפתח.
\n", + " במילים אחרות: אם ארצה לשנות עמודה מסוימת בטבלה, אסור שזה ישפיע על התוכן של עמודה אחרת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לדוגמה, יצרתי טבלת סרטים שמורכבת ממספר סרט ושמו.
\n", + " הוספתי לטבלה שתי עמודות: שחקן וגובה השחקן.
\n", + " לאחר שהכנסנו קצת נתונים לטבלה הגיעו לאוזנינו שמועות שאחד השחקנים התפטר, ובמקומו הגיע שחקן חדש.
\n", + " שינינו את שמו של השחקן הישן לשחקן החדש, הפלנו את העט וטסנו הביתה לאכול סושי.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מה ששכחנו זה שיש לשחקן גם גובה, ואותו לא עדכנו.
\n", + " למעשה בניסיון לעדכון השחקן שברנו את אמינות הנתונים במסד הנתונים שלנו.
\n", + " כל זה היה יכול להימנע אם הייתה טבלה נפרדת של שחקנים, ורק הייתי צריך לעדכן בעמודת המפתח הזר את ה־id של השחקן הנכון.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הצורה הנורמלית השלישית, אם כך, דורשת שכל עמודה בטבלה תהיה תלויה במפתח הראשי, ולא באף עמודה אחרת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ועוד" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " יש צורות נורמליות נוספות (BCNF, 4NF, 5NF, DKNF, 6NF) עליהן אפשר לקרוא כאן.
\n", + " לדעתנו, הדבר החשוב ביותר זה להבין את העקרונות הכלליים, ולא לעקוב שורה שורה אחרי חוקי הנרמול.
\n", + " הפרידו סוגים שונים של ישויות לטבלאות, ותמיד חשבו על המשתמש במסד הנתונים.
\n", + " כמה קל יהיה לו לתשאל את מסד הנתונים? כמה קל יהיה לו לשנות מידע? האם בעקבות פעולות עדכון המידע עלול להיהרס? \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## סיכום" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במחברת זו למדנו להכיר מונחים חשובים בעולם של מסדי נתונים רלציוניים.
\n", + " לצד המונחים הבסיסיים שהכרנו, כמו מפתח ראשי, מפתח זר וקשרי גומלין, למדנו ליצור מסד נתונים מנורמל.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ישנה חשיבות רבה ליצירה של מסד נתונים מנורמל היטב.
\n", + " מסד נתונים שכזה יהיה גמיש להרחבות עתידיות, יאפשר אחסון ושליפה בצורה יעילה ונוחה ויחסוך תקלות רבות בתפעולו.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " זכרו שלא צריך לזכור כללי נרמול אקדמיים ומסובכים. מספיק להכיר את הרעיונות הבסיסיים ולהשתמש בהיגיון.
\n", + " אם היינו צריכים לבחור כלל אחד לזכור בקשר לנרמול, הוא היה זה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " הקפידו שכל טבלה במסד הנתונים שלכם (חוץ מהטבלאות המקשרות) תאגד נתונים שקשורים לנושא אחד בלבד,
\n", + " ושכל הנתונים בטבלה יתייחסו אך ורק לנושא הזה.
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### This Is How We Do" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בתכל'ס , כשאנחנו באים לבנות מסד נתונים, אלו השלבים שאנחנו עוברים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "
  1. לחשוב בגדול על אילו סוגי ישויות הולכים להיות לנו במסד הנתונים. מהם אנחנו יוצרים שמות לטבלאות.
  2. \n", + "
  3. לחשוב אילו תכונות יהיו לכל אחת מהישויות. מהן אנחנו יוצרים את העמודות בכל טבלה.
  4. \n", + "
  5. בכל טבלה, עוברים ומסמנים איזו עמודה מזהה באופן חד־חד ערכי את השורות. אם אין כזו, מוסיפים שדה עם מספר רץ.
    \n", + " זה השדה שמסומן כמפתח ראשי.
  6. \n", + "
  7. מוסיפים אילוצים: בודקים באילו שדות לא יכול להיות NULL, לדוגמה.
    \n", + " אם תרצו, קראו גם על UNIQUE ועל CHECK.
  8. \n", + "
  9. מציירים קשרי גומלין. אם רואים שיש קשר רבים לרבים, בונים טבלה מקשרת.
  10. \n", + "
  11. מביטים על התרשים ועוברים על כל השלבים מחדש כדי לראות שלא שכחנו משהו והכל נראה טוב.
    \n", + " אם יש צורך לפרק עמודה או למנוע כפילויות – זה מה שעושים.
  12. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## מונחים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
נרמול מסד נתונים
\n", + "
\n", + " באנגלית: Database normalization.
\n", + " סידור מבנה מסד הנתונים באופן מיטבי, שיאפשר גמישות וימזער סיכונים לטעויות ואובדן מידע.\n", + "
\n", + "
אי־פריקות
\n", + "
\n", + " באנגלית: Atomic data.
\n", + " הרעיון לפיו כל תא במסד הנתונים צריך להיות מורכב ממידע שאי אפשר לפרקו לחלקים קטנים יותר.\n", + "
\n", + "
חֲרִיגוּת
\n", + "
\n", + " באנגלית: Anomaly.
\n", + " תקלה שנגרמת כתופעת לוואי של פעולת עדכון, הוספה או הסרה של מידע, במסד נתונים לא מנורמל דיו.
\n", + " חריגות מחיקה, לדוגמה, היא תופעה שנגרמת ממחיקת ישות אחת שבעקבותיה נעלמת ישות אחרת.\n", + "
\n", + "
אילוץ
\n", + "
\n", + " באנגלית: Constraint.
\n", + " חוקים שנאכפים על המידע שמוזן לעמודה, ומגבילים את המידע שאפשר להכניס לתוכה.
\n", + " האילוץ UNIQUE, לדוגמה, אוסר על ערך להופיע פעמיים בעמודה. NOT NULL לא מאפשר לערך שמוזן לעמודה להיות NULL.\n", + "
\n", + "
קשר גומלין
\n", + "
\n", + " באנגלית: Relationship.
\n", + " חיבור רעיוני בין עמודות בבסיס הנתונים, שמראה על קשר בין עמודה אחת לאחרת.
\n", + " החיבור מאפשר למסד הנתונים לשפר ביצועים, ולוודא שנתונים שהוכנסו בשדה מסוים קיימים בשדה אחר.
\n", + " לדוגמה: קשר שהוגדר בין טבלת סרטים לבין טבלת שחקנים, יוודא ביצירת סרט השחקנים שהוזנו אכן קיימים במסד הנתונים..
\n", + " קשר יכול להיות יחיד ליחיד, יחיד לרבים או רבים לרבים.\n", + "
\n", + "
מפתח ראשי
\n", + "
\n", + " באנגלית: Primary key.
\n", + " עמודה (או מספר עמודות) שמזהות את הישות שבטבלה באופן חד־חד ערכי.
\n", + " לרוב, המפתח הראשי הוא מספר טבעי רץ – 1 עבור הישות הראשונה בטבלה, 2 עבור הישות השנייה וכן הלאה.\n", + "
\n", + "
מפתח זר
\n", + "
\n", + " באנגלית: Foreign key.
\n", + " עמודה שבה הערכים מתייחסים לישות מטבלה אחרת, באמצעות ציון הערכים במפתח הראשי של אותה טבלה.
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## תרגילים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגילים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " צרו ERD מלא למערכת הגשת התרגילים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ה־ERD צריך להתחשב בכך שמסד הנתונים אמור להכיל את הנתונים הבאים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לצורך יצירת ה־ERD תוכלו להשתמש ב־vuerd, או בכל כלי אחר שתמצאו שנוח לכם.\n", + "

" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week13/5_Joins.ipynb b/week13/5_Joins.ipynb new file mode 100644 index 0000000..03e8124 --- /dev/null +++ b/week13/5_Joins.ipynb @@ -0,0 +1,2903 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"לוגו" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# שאילתות לצירוף טבלאות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## הקדמה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " עד כה, הסקנו תובנות ממסד הנתונים שלנו בעזרת שאילתות שהשתמשו כל פעם בנתונים מטבלה אחת.
\n", + " השאילתות שבנינו תשאלו טבלה מסוימת, והשתמשו במידע הקיים בה כדי לענות על שאלות מעניינות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשיעור הקודם למדנו על הצורך בביצוע נרמול למסד הנתונים, שמפזר מידע על פני כמה טבלאות.
\n", + " מצד אחד, הנרמול מסייע לנו לשמור מידע בצורה חסכונית ויעילה,
\n", + " ומצד שני – בשיטה שלמדנו עד כה אי אפשר לתשאל מידע ששמור ביותר מטבלה אחת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כיוון שבחיים האמיתיים לרוב מסדי הנתונים שנתשאל יהיו (בתקווה) מנורמלים,
\n", + " נצטרך למצוא דרך להתייחס למידע שפרוש על פני כמה טבלאות במסד הנתונים שלנו.
\n", + " זה מה שנלמד במחברת הקרובה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לפני שנתחיל, ניזכר כיצד נראה מסד הנתונים שעליו אנחנו עובדים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בתמונה\n", + "
\n", + " תרשים שמציג את כל הטבלאות והעמודות במסד הנתונים, כולל את הקשרים ביניהם.
\n", + " המינוח המקצועי לתרשים שכזה הוא \"תרשים ישויות קשרים\" (באנגלית: ERD).\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## הקדמה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הצורך שעליו נרצה לענות במחברת הקרובה, כמו שעשינו עד עתה, הוא מציאת תשובות לשאלות.
\n", + " במחברת הזו, בניגוד למחברות הקודמות, כל שאלה תתייחס לנתונים שפזורים על פני כמה טבלאות.
\n", + " באיזה סרט שיחקו הכי הרבה שחקנים, ואיזה סרט תורגם להכי הרבה שפות?
\n", + " אילו שחקנים שיחקו במטריקס, ומה היה הסרט המצליח ביותר בו שיחק ג'ון טרבולטה?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי לענות על השאלות שהוצגו מעלה, נצטרך לתשאל יותר מטבלה אחת בכל פעם.
\n", + " דרך מקובלת לעשות זאת היא לצרף כמה טבלאות כך שיוצגו כטבלה אחת עם כל הנתונים שלנו, ולתשאל אותה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בשיעור הקרוב נשחק עם הרעיון של צירוף טבלאות.
\n", + " בעזרת צירוף טבלאות, נוכל ליצור טבלה אחת שאותה יהיה קל לתשאל.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי להתחיל, נדמיין שתי טבלאות שישמשו לנו לדוגמה.
\n", + " אלו יהיו טבלאות פשוטות יחסית: טבלת \"צורות\" וטבלת \"בניינים\". \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בציור\n", + "
\n", + " שתי טבלאות: צורות ובניינים.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " המשמעות של הביטוי \"צירוף בין הטבלאות\" עשויה להיות מעורפלת בשלב הזה.
\n", + " הכוונה, במקרה שלנו, היא ליצור טבלה זמנית שבנויה גם מהעמודות של טבלת צורות, וגם מהעמודות של טבלת בניינים.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " איך זה ייראה?
\n", + " ובכן, גם ההגדרה הזו קצת מעורפלת, ובפסקאות הבאות נסקור יותר מדרך אחת לצמד בין הטבלאות.
\n", + " לפני שניגש לעשות זאת, נסו לחשוב בעצמכם על כמה דרכים מועילות ליצור טבלה אחת משתי הטבלאות שמופיעות מעלה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## מכפלה קרטזית" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לפני שנתחיל ללמוד על הדרכים המועילות לצמד בין הטבלאות, נלמד על רעיון תיאורטי חשוב.
\n", + " הרעיון נקרא \"מכפלה קרטזית\", ובהשאלה לטבלאות שלנו – זו דרך מעניינת לצמד בין הישויות בטבלאות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי לבצע מכפלה קרטזית, אנחנו פועלים לפי האלגוריתם הבא:\n", + "

\n", + "\n", + "
    \n", + "
  1. בחר את הישות (שורה) הראשונה בטבלה השמאלית שעדיין לא ביצעת עבורה את התהליך שמופיע להלן. נקרא לה X.
  2. \n", + "
  3. עבור כל ישות בטבלה הימנית, צור ישות חדשה שמורכבת מ־X והישות בטבלה הימנית.
  4. \n", + "
  5. חזור לסעיף 1, ובצע את התהליך עד שלא ישארו יותר שורות בטבלה השמאלית שעבורן לא ביצענו את התהליך.
  6. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כך נבצע את התהליך עבור הטבלאות בניינים וצורות:
\n", + " לשורה הראשונה, בה כתוב \"עיגול\", נצמיד את כל הבניינים – עזריאלי 1, עזריאלי 2 והפנטגון, וניצור עבור כל בניין שורה חדשה.
\n", + " כך גם עבור המשולש (3 שורות, עבור 3 בניינים), עבור המעוין ועבור הריבוע.
\n", + " עבור כל איבר בשמאל, נצמד את כל האיברים בימין. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " התוצאות יראו כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בציור\n", + "
\n", + " תרשים שמציג את התהליך והתוצאות של מכפלה קרטזית בין טבלת צורות לטבלת בניינים.
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " לפניכם מוגדרות הטבלאות שהובאו כדוגמה.
\n", + " נסו לכתוב תוכנית פייתון שמבצעת מכפלה קרטזית בין הטבלאות.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "SHAPES = (\n", + " ['◯', 'Circle', 0],\n", + " ['△', 'Triangle', 3],\n", + " ['♢', 'Rhombus', 4],\n", + " ['□', 'Square', 4],\n", + ")\n", + "\n", + "\n", + "BUILDINGS = (\n", + " ['Azrieli 1', 'Circle', 'Derech Menahem Begin 132'],\n", + " ['Azrieli 2', 'Triangle', 'Derech Menahem Begin 132'],\n", + " ['The Pentagon', 'Well, Pentagon',\n", + " 'Arlington, Virginia, Washington Blvd 100'],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ב־SQL, נוכל להשיג תוצאה של מכפלה קרטזית בין שתי טבלאות בעזרת CROSS JOIN.
\n", + " זה יראה כך (השוו לתוצאות האיור שלמעלה):\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM צורות \n", + " CROSS JOIN בניינים;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מתי השימוש ב־CROSS JOIN יכול להועיל?
\n", + " בעיקר כשאנחנו מעוניינים לקבל תובנה על כל השילובים האפשריים של שורות בין שתי טבלאות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם, לדוגמה, אנחנו מנהלים מסעדה, ואנחנו רוצים להציע לאורחים שלנו ארוחת בוקר עסקית.
\n", + " תמחור הארוחה ללקוח יהיה 100 ש\"ח, כשהארוחה כוללת שתייה, מנה עיקרית ועיתון.
\n", + " חשוב לנו לעשות רווח של לפחות 40 שקלים על כל ארוחה כזו שנמכרת.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בתפריט יש לנו 5 אפשרויות לשתייה, 6 אפשרויות למנה עיקרית ו־3 אפשרויות לעיתונים.
\n", + " העלות של המוצרים והמנות עבור הבעלים של המסעדה לא אחידה.
\n", + " איך נמצא שילובים בעייתיים שלא יניבו רווח של 40 שקלים?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הנתונים מסודרים בטבלאות במסד הנתונים שקיים בקובץ resources/restaurant.db.
\n", + " נראה את האפשרויות השונות, והעלות של כל אחת מהן לבעלי המסעדה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת drinks
idnamecost
1Coffee10
2Orange Juice9
3Nuka-Cola14
4Janx Shot4.2
5Milk of the Poppy11
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת foods
idnamecost
1Caesar Salad36
2Xiaolongbao43
3Krabby Patties39
4Butterscotch Cinnamon Pie40
5Lava Soup34
6Lembas43
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
טבלת newspapers
idnamecost
1The Washington Herald12
2The Springfield Shopper8
3Daily Bugle9
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם נצטרף את הטבלאות באמצעות CROSS JOIN, נקבל את כל השילובים שהלקוח יכול לבחור מהתפריט.
\n", + " נכתוב זאת כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM drinks\n", + " CROSS JOIN foods\n", + " CROSS JOIN newspapers;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " התוצאות יראו כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
id name cost id name cost id name cost total_cost
1 Coffee 10 1 Caesar Salad 36 1 The Washington Herald 12 58
1Coffee101Caesar Salad362The Springfield Shopper854
1Coffee101Caesar Salad363Daily Bugle955
1Coffee102Xiaolongbao431The Washington Herald1265
1Coffee102Xiaolongbao432The Springfield Shopper861
1Coffee102Xiaolongbao433Daily Bugle962
1Coffee103Krabby Patties391The Washington Herald1261
1Coffee103Krabby Patties392The Springfield Shopper857
1Coffee103Krabby Patties393Daily Bugle958
1Coffee104Butterscotch Cinnamon Pie401The Washington Herald1262
1Coffee104Butterscotch Cinnamon Pie402The Springfield Shopper858
1Coffee104Butterscotch Cinnamon Pie403Daily Bugle959
1Coffee105Lava Soup341The Washington Herald1256
1Coffee105Lava Soup342The Springfield Shopper852
1Coffee105Lava Soup343Daily Bugle953
1Coffee106Lembas431The Washington Herald1265
1Coffee106Lembas432The Springfield Shopper861
1Coffee106Lembas433Daily Bugle962
2Orange Juice91Caesar Salad361The Washington Herald1257
2Orange Juice91Caesar Salad362The Springfield Shopper853
2Orange Juice91Caesar Salad363Daily Bugle954
2Orange Juice92Xiaolongbao431The Washington Herald1264
2Orange Juice92Xiaolongbao432The Springfield Shopper860
2Orange Juice92Xiaolongbao433Daily Bugle961
2Orange Juice93Krabby Patties391The Washington Herald1260
2Orange Juice93Krabby Patties392The Springfield Shopper856
2Orange Juice93Krabby Patties393Daily Bugle957
2Orange Juice94Butterscotch Cinnamon Pie401The Washington Herald1261
2Orange Juice94Butterscotch Cinnamon Pie402The Springfield Shopper857
2Orange Juice94Butterscotch Cinnamon Pie403Daily Bugle958
2Orange Juice95Lava Soup341The Washington Herald1255
2Orange Juice95Lava Soup342The Springfield Shopper851
2Orange Juice95Lava Soup343Daily Bugle952
2Orange Juice96Lembas431The Washington Herald1264
2Orange Juice96Lembas432The Springfield Shopper860
2Orange Juice96Lembas433Daily Bugle961
3Nuka-Cola141Caesar Salad361The Washington Herald1262
3Nuka-Cola141Caesar Salad362The Springfield Shopper858
3Nuka-Cola141Caesar Salad363Daily Bugle959
3Nuka-Cola142Xiaolongbao431The Washington Herald1269
3Nuka-Cola142Xiaolongbao432The Springfield Shopper865
3Nuka-Cola142Xiaolongbao433Daily Bugle966
3Nuka-Cola143Krabby Patties391The Washington Herald1265
3Nuka-Cola143Krabby Patties392The Springfield Shopper861
3Nuka-Cola143Krabby Patties393Daily Bugle962
3Nuka-Cola144Butterscotch Cinnamon Pie401The Washington Herald1266
3Nuka-Cola144Butterscotch Cinnamon Pie402The Springfield Shopper862
3Nuka-Cola144Butterscotch Cinnamon Pie403Daily Bugle963
3Nuka-Cola145Lava Soup341The Washington Herald1260
3Nuka-Cola145Lava Soup342The Springfield Shopper856
3Nuka-Cola145Lava Soup343Daily Bugle957
3Nuka-Cola146Lembas431The Washington Herald1269
3Nuka-Cola146Lembas432The Springfield Shopper865
3Nuka-Cola146Lembas433Daily Bugle966
4Janx Shot4.21Caesar Salad361The Washington Herald1252.2
4Janx Shot4.21Caesar Salad362The Springfield Shopper848.2
4Janx Shot4.21Caesar Salad363Daily Bugle949.2
4Janx Shot4.22Xiaolongbao431The Washington Herald1259.2
4Janx Shot4.22Xiaolongbao432The Springfield Shopper855.2
4Janx Shot4.22Xiaolongbao433Daily Bugle956.2
4Janx Shot4.23Krabby Patties391The Washington Herald1255.2
4Janx Shot4.23Krabby Patties392The Springfield Shopper851.2
4Janx Shot4.23Krabby Patties393Daily Bugle952.2
4Janx Shot4.24Butterscotch Cinnamon Pie401The Washington Herald1256.2
4Janx Shot4.24Butterscotch Cinnamon Pie402The Springfield Shopper852.2
4Janx Shot4.24Butterscotch Cinnamon Pie403Daily Bugle953.2
4Janx Shot4.25Lava Soup341The Washington Herald1250.2
4Janx Shot4.25Lava Soup342The Springfield Shopper846.2
4Janx Shot4.25Lava Soup343Daily Bugle947.2
4Janx Shot4.26Lembas431The Washington Herald1259.2
4Janx Shot4.26Lembas432The Springfield Shopper855.2
4Janx Shot4.26Lembas433Daily Bugle956.2
5Milk of the Poppy111Caesar Salad361The Washington Herald1259
5Milk of the Poppy111Caesar Salad362The Springfield Shopper855
5Milk of the Poppy111Caesar Salad363Daily Bugle956
5Milk of the Poppy112Xiaolongbao431The Washington Herald1266
5Milk of the Poppy112Xiaolongbao432The Springfield Shopper862
5Milk of the Poppy112Xiaolongbao433Daily Bugle963
5Milk of the Poppy113Krabby Patties391The Washington Herald1262
5Milk of the Poppy113Krabby Patties392The Springfield Shopper858
5Milk of the Poppy113Krabby Patties393Daily Bugle959
5Milk of the Poppy114Butterscotch Cinnamon Pie401The Washington Herald1263
5Milk of the Poppy114Butterscotch Cinnamon Pie402The Springfield Shopper859
5Milk of the Poppy114Butterscotch Cinnamon Pie403Daily Bugle960
5Milk of the Poppy115Lava Soup341The Washington Herald1257
5Milk of the Poppy115Lava Soup342The Springfield Shopper853
5Milk of the Poppy115Lava Soup343Daily Bugle954
5Milk of the Poppy116Lembas431The Washington Herald1266
5Milk of the Poppy116Lembas432The Springfield Shopper862
5Milk of the Poppy116Lembas433Daily Bugle963
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " חדי העין שמו לב שהוספנו לתוצאות עמודה נוספת בשם total_cost – עלות הארוחה לבעלי המסעדה.
\n", + " זו השאילתה שבה השתמשנו כדי להשיג את העלות הזו:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *,\n", + " drinks.cost + foods.cost + newspapers.cost AS total_cost\n", + "FROM drinks\n", + " CROSS JOIN foods\n", + " CROSS JOIN newspapers;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שימו לב לצורת ההתייחסות לעמודות בעזרת הנקודה.
\n", + " מסד הנתונים לא יכול לדעת לאיזה עמודת cost אנחנו מתייחסים בכל פעם.
\n", + " לכן ציינו במפורש לפני כל שם עמודה את הטבלה ממנה היא מגיעה, בצורה table_name.column_name.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נראה, אם כך, שיש לא מעט מנות שהרווח עליהן הוא פחות מ־40 שקלים.
\n", + " נוכל לראות את כמות השילובים ההפסדיים בעזרת השאילתה הבאה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT COUNT(*)\n", + "FROM drinks\n", + " CROSS JOIN foods\n", + " CROSS JOIN newspapers\n", + "WHERE drinks.cost + foods.cost + newspapers.cost + 40 > 100;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " מה הרווח הנמוך ביותר שבעל המסעדה יעשה על ארוחה עסקית, אם הלקוח יבחר את המוצרים היקרים ביותר?
\n", + " כתבו שאילתה אחת שעונה על השאלה.\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אחרי שהשתעשענו, נספר בלחישה של־CROSS JOIN יש מעט מאוד שימושים בעולם האמיתי.
\n", + " השימוש העיקרי בו, עבורנו לפחות, הוא ללמד אתכם על INNER JOIN בקלות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## INNER JOIN" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### המטרה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " השימוש ב־CROSS JOIN יצר כמות עצומה של תוצאות.
\n", + " עבור 4 טבלאות שבכל אחת מהן יש 100 רשומות, CROSS JOIN יחזיר 100,000,000 רשומות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " רוב הפעמים, אנחנו לא צריכים את כל השילובים בין עמודות – אלא רק שילובים שיועילו לנו.
\n", + " אם נביט שוב בטבלאות הדוגמה שלנו, נגלה עמודה שמשותפת לשתי הטבלאות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בציור\n", + "
\n", + " טבלאות הדוגמה – צורות ובניינים. בשתיהן יש עמודה שמציינת את הצורה באופן מילולי.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נרצה ליצור טבלה חדשה – צירוף של הטבלה צורות עם הטבלה בניינים.
\n", + " נתחיל בלבצע מכפלה קרטזית בין הטבלאות,
\n", + " ואז נסיר את כל השורות שבהן העמודה שם שבטבלת צורות והעמודה צורה שבטבלת בניינים לא תואמות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כלומר, מתוצאות המכפלה הקרטזית נבחר רק את השורות שבהן הצורה בשתי הטבלאות זהה.
\n", + " נראה תוצאה לדוגמה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בציור\n", + "
\n", + " דוגמה לצירוף טבלאות במקומות שבהן יש ערך זהה בעמודות הנבחרות.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בתוצאות של הטבלה החדשה שנוצרה הושמטו שורות רבות:
\n", + " תחילה, הושמטו כל השורות מטבלת צורות שאין להן שורות עם צורה תואמת בטבלת בניינים.
\n", + " באותו אופן, הושמטו גם השורות מטבלת בניינים עבורן אין צורה עם שם תואם בטבלת צורות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " פרט חשוב במיוחד הוא שעבור כל שורה יכולה להיות יותר מהתאמה אחת.
\n", + " לדוגמה, אם נוסיף לטבלת \"בניינים\" עוד בניין שצורתו ריבוע,
\n", + " התוצאות יכללו שני בניינים שצורתם \"ריבוע\" בטבלת התוצאות – גם את הבניין שהוספנו, וגם את הבניין שהיה שם:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בציור\n", + "
\n", + " ייתכן מצב בו שורה מאחת הטבלאות תתאים ליותר משורה אחת בטבלה השנייה.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם היינו מוסיפים לטבלת צורות, סתם בשביל הניסוי, שורה נוספת ששמה \"ריבוע\" עם תמונה של קרוקודיל,
\n", + " היינו מקבלים שתי שורות נוספות עם תמונת קרוקודיל בטבלת התוצאות – אחת עבור עזריאלי 2 ואחת עבור הבית של שרלוק.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נסכם:
\n", + " בסוג החדש הזה של צירוף בין טבלאות, לקחנו את המכפלה הקרטזית בין שתי הטבלאות.
\n", + " לפני שהחזרנו את התוצאות, הסתכלנו על עמודה מסוימת מכל אחת מהטבלאות, והגדרנו את הקשר שאנחנו מחפשים שיהיה ביניהן.
\n", + " רשומות שלא קיימו את הקשר שהגדרנו לא הופיעו כתוצאה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### התחביר" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הצורה הזו לחיבור טבלאות, בה אנחנו מסננים את כל השורות שלא מתאימות לתנאי שלנו, נקראת INNER JOIN.
\n", + " היא ככל הנראה צורת ה־JOIN השימושית ביותר שנלמד.
\n", + " מבחינה תחבירית היא נראית כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM צורות\n", + " INNER JOIN בניינים\n", + " ON בניינים.צורה = צורות.שם;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בניגוד ל־CROSS JOIN, בה פשוט ביקשנו להצליב בין שתי טבלאות,
\n", + " כאן אנחנו מצרפים את מילת המפתח ON, שעובדת פחות או יותר כמו פסוקית WHERE.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אפשר לדמיין את מה שקורה מאחורי הקלעים בצורה הבאה:\n", + "

\n", + "\n", + "
    \n", + "
  1. בצע מכפלה קרטזית (CROSS JOIN) בין שתי הטבלאות.
  2. \n", + "
  3. מהתוצאות, אסוף רק את השורות בהן הערך בעמודה שם שבטבלת צורות זהה לערך בעמודה צורה שבטבלת בניינים.
  4. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הביטו שוב באיור שלמעלה וודאו שאתם מבינים איך INNER JOIN עובד.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### דוגמאות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כדי להבין את הפוטנציאל המשוגע שגלום ב־INNER JOIN,
\n", + " ננסה להבין אילו בעלי תפקידים השתתפו בסרט \"The Lego Movie\".
\n", + " לצורך כך נצרף את טבלת principals לטבלת movies:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies\n", + " INNER JOIN principals\n", + " ON movies.id = principals.movie_id\n", + "WHERE movies.original_title = 'The Lego Movie';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מה עשינו כאן?
\n", + " ביקשנו לצרף ליד כל שורה בטבלת movies שורה מטבלת principals,
\n", + " בכל מקום שבו ה־movie_id שלהם תואם.
\n", + " מתוך התוצאות, שלפנו רק את השורות שבהן שם הסרט הוא \"The Lego Movie\".\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לפניכם תוצאות השאילתה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idtitleoriginal_titleyearavg_votevotesdurationbudgetgross_incomemovie_idorderingname_idjob_idcharacters
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt14900171nm06954352Emmet Brickowski
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt149001710nm92040847 
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt14900172nm00020712Lord Business,President Business,The Man Upstairs
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt14900173nm00069691Wyldstyle,Lucy
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt14900174nm00047152Batman,Bruce Wayne
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt14900175nm05880873 
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt14900176nm05204883 
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt14900177nm10879527 
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt14900178nm11569847 
tt1490017The Lego MovieThe Lego Movie2,0147.7319,327100\\$ 60000000\\$ 468060692tt14900179nm92040837 
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " פסוקית ה־JOIN תתבצע תמיד ישירות אחרי פסוקית ה־FROM,
\n", + " כך שמדובר בשאילתה רגילה לכל דבר, רק שהרחבנו קצת את הטבלה שעליה אנחנו שולפים.
\n", + " עכשיו נוכל לספור כמה בעלי תפקידים היו בסרט באמצעות COUNT, כרגיל:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT COUNT(*) AS lego_movie_principals\n", + "FROM movies\n", + " INNER JOIN principals\n", + " ON movies.id = principals.movie_id\n", + "WHERE movies.original_title = 'The Lego Movie';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תעלול נחמד" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " זה הזמן ללמוד תעלול נחמד.
\n", + " בשיעורים הקודמים השתמשנו במילת המפתח AS כדי לערוך את שמן של עמודות.
\n", + " רבים משתמשים באותה שיטה כדי לקצר את שמות הטבלאות שלהם, ולהפוך את השאילתה לנוחה יותר לעין.
\n", + " אפשר לנקוט באותה צורה גם פה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נדגים דרך קצרה אפילו יותר – פשוט נרשום את השם המקוצר אחרי שם הטבלה.
\n", + " זה נראה כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies m\n", + " INNER JOIN principals p\n", + " ON m.id = p.movie_id\n", + "WHERE m.original_title = 'The Lego Movie';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " יש לא מעט אנשים שמעדיפים להשאיר את מילת המפתח AS בקוד.
\n", + " לשיטתם, צורת הכתיבה הזו נקייה יותר ומאפשרת להפריד חזותית בין שם הטבלה לבין הקיצור שמייצג אותה.
\n", + " אנחנו בחרנו להשאיר את מילת המפתח AS בהמשך ההסברים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### צירוף טבלאות מרובות" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ננסה להציג את שמות כל בעלי התפקידים ב־The Lego Movie.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כשחושבים על זה? אתם מסוגלים לעשות את זה לבד.
\n", + " (אהמ... רמז... כותרת...)\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " הציגו את שמות כל בעלי התפקידים ב־The Lego Movie.
\n", + " ליד שמם, הציגו מה היה התפקיד שלהם.\n", + "

\n", + "
\n", + "
\n", + "

\n", + " חשוב!
\n", + " פתרו לפני שתמשיכו!\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " קודם כול, ננקה את השאילתה ונצמצם שדות לא מועילים.
\n", + " נציין בפסוקית ה־SELECT רק את העמודות שעשויות להועיל לנו, בינתיים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT m.id, m.original_title, p.name_id, p.job_id, p.\"characters\"\n", + "FROM movies AS m\n", + " INNER JOIN principals AS p\n", + " ON m.id = p.movie_id\n", + "WHERE m.original_title = 'The Lego Movie';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ונקבל את התוצאה הסתומה הבאה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
idoriginal_titlename_idjob_idcharacters
tt1490017The Lego Movienm06954352Emmet Brickowski
tt1490017The Lego Movienm92040847 
tt1490017The Lego Movienm00020712Lord Business,President Business,The Man Upstairs
tt1490017The Lego Movienm00069691Wyldstyle,Lucy
tt1490017The Lego Movienm00047152Batman,Bruce Wayne
tt1490017The Lego Movienm05880873 
tt1490017The Lego Movienm05204883 
tt1490017The Lego Movienm10879527 
tt1490017The Lego Movienm11569847 
tt1490017The Lego Movienm92040837 
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בתוצאה אנחנו רואים את כל השחקנים ששיחקו ב־The Lego Movie ואת תפקידיהם.
\n", + " העניין הוא שהטבלה נורמלה, כך שהשם האמיתי והשם המילולי של העבודה מופיעים בטבלאות אחרות. \n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בתוצאה הזו נראה שיש לנו את המספרים הסידוריים של שם בעל התפקיד וסוג העבודה שהוא מבצע.
\n", + " ניגש למלאכה של חילוץ \"הערכים האמיתיים\" של כל אחד מהם.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ראשית נטפל בעמודה job_id.
\n", + " לפי ה־ERD, נוכל לראות שהיא מפתח זר למפתח הראשי id בטבלת jobs.
\n", + " נצרף את הטבלאות אחת לשנייה בעזרת INNER JOIN, ונוסיף ל־SELECT את השדות שצורפו מטבלת jobs.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT m.id, m.original_title, p.name_id, p.job_id, p.\"characters\", j.id, j.name\n", + "FROM movies AS m\n", + " INNER JOIN principals AS p\n", + " ON m.id = p.movie_id\n", + " INNER JOIN jobs AS j\n", + " ON p.job_id = j.id\n", + "WHERE m.original_title = 'The Lego Movie';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שימו לב שלא חידשנו פה שום דבר הנוגע ל־JOIN – פשוט צירפנו עוד טבלה לחגיגה.
\n", + " אחרי שהשגנו את job_id מטבלת principals, התבקש ממש שנשתמש בו אל מול טבלת jobs.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שימו לב שבפסוקית ה־SELECT ביקשנו להציג את כל השדות של jobs.
\n", + " ביטוי מקוצר נחמד שישיג תוצאה זהה הוא j.*, שמסמל את כל העמודות הקיימות בטבלה j.
\n", + " נדגים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT m.id, m.original_title, p.name_id, p.job_id, p.\"characters\", j.*\n", + "[...]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " התוצאות שחזרו הן:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
idoriginal_titlename_idjob_idcharactersidname
tt1490017The Lego Movienm06954352Emmet Brickowski2actor
tt1490017The Lego Movienm00020712Lord Business,President Business,The Man Upstairs2actor
tt1490017The Lego Movienm00069691Wyldstyle,Lucy1actress
tt1490017The Lego Movienm00047152Batman,Bruce Wayne2actor
tt1490017The Lego Movienm05880873 3director
tt1490017The Lego Movienm05204883 3director
tt1490017The Lego Movienm10879527 7writer
tt1490017The Lego Movienm11569847 7writer
tt1490017The Lego Movienm92040837 7writer
tt1490017The Lego Movienm92040847 7writer
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " מצוין!
\n", + " בטבלה שלמעלה אנחנו רואים מה היה תפקידם של אותם בעלי תפקידים מפורסמים שעזרו ביצירה של The Lego Movie.
\n", + " עכשיו נחשוף שמותיהם של אותם בעלי תפקידים מ־names, באותה צורה שבה עשינו זאת עם jobs.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " נסו לצרף לשאילתה הנוכחית את שמות בעלי התפקידים שסייעו בהפקת The Lego Movie.\n", + "

\n", + "
\n", + "
\n", + "

\n", + " חשוב!
\n", + " פתרו לפני שתמשיכו!\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בצירוף של טבלת names אין שום חידוש.
\n", + " נעשה בדיוק מה שעשינו עם טבלת jobs, רק שהפעם נשתמש בפרטים של טבלת names.
\n", + " נחבר את הטבלאות לפי העמודות principals.name_id ו־names.id:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT m.id, m.original_title, p.name_id, p.job_id, p.\"characters\", j.*, n.*\n", + "FROM movies AS m\n", + " INNER JOIN principals AS p\n", + " ON m.id = p.movie_id\n", + " INNER JOIN jobs AS j\n", + " ON p.job_id = j.id\n", + " INNER JOIN names AS n\n", + " ON p.name_id = n.id\n", + "WHERE m.original_title = 'The Lego Movie';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " והנה התוצאות:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
idoriginal_titlename_idjob_idcharactersidnameidnameheightdate_of_birthdate_of_deathchildren
tt1490017The Lego Movienm06954352Emmet Brickowski2actornm0695435Chris Pratt1881979-06-21 00:00:00.000000 2
tt1490017The Lego Movienm00020712Lord Business,President Business,The Man Upstairs2actornm0002071Will Ferrell1911967-07-16 00:00:00.000000 3
tt1490017The Lego Movienm00069691Wyldstyle,Lucy1actressnm0006969Elizabeth Banks1641974-02-10 00:00:00.000000 2
tt1490017The Lego Movienm00047152Batman,Bruce Wayne2actornm0004715Will Arnett1891970-05-04 00:00:00.000000 2
tt1490017The Lego Movienm05880873 3directornm0588087Christopher Miller1801975-09-23 00:00:00.000000 1
tt1490017The Lego Movienm05204883 3directornm0520488Phil Lord1771975-07-12 00:00:00.000000 0
tt1490017The Lego Movienm10879527 7writernm1087952Dan Hageman   0
tt1490017The Lego Movienm11569847 7writernm1156984Kevin Hageman 1974-04-21 00:00:00.000000 0
tt1490017The Lego Movienm92040837 7writernm9204083Ole Kirk Christiansen 1891-04-01 00:00:00.0000001958-03-11 00:00:00.0000000
tt1490017The Lego Movienm92040847 7writernm9204084Godtfred Kirk Christiansen 1920-07-08 00:00:00.0000001995-07-13 00:00:00.0000000
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " ננקה שדות לא נחוצים כדי לקבל בדיוק את מה שרצינו, בלי נתונים שמפריעים בעין:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT m.original_title as movie_name, \n", + "\t n.name as principal_name,\n", + "\t j.name as job,\n", + "\t p.\"characters\"\n", + "FROM movies AS m\n", + " INNER JOIN principals AS p\n", + " ON m.id = p.movie_id\n", + " INNER JOIN jobs AS j\n", + " ON p.job_id = j.id\n", + " INNER JOIN names AS n\n", + " ON p.name_id = n.id\n", + "WHERE m.original_title = 'The Lego Movie';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " והנה התוצאות במלוא תפארתן:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
movie_nameprincipal_namejobcharacters
The Lego MovieChris PrattactorEmmet Brickowski
The Lego MovieWill FerrellactorLord Business,President Business,The Man Upstairs
The Lego MovieElizabeth BanksactressWyldstyle,Lucy
The Lego MovieWill ArnettactorBatman,Bruce Wayne
The Lego MovieChristopher Millerdirector 
The Lego MoviePhil Lorddirector 
The Lego MovieDan Hagemanwriter 
The Lego MovieKevin Hagemanwriter 
The Lego MovieOle Kirk Christiansenwriter 
The Lego MovieGodtfred Kirk Christiansenwriter 
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כיוון ש־INNER JOIN היא צורת JOIN כה שימושית,
\n", + " ברירת המחדל במסדי נתונים היא שאם כותבים פשוט JOIN הכוונה היא ל־INNER JOIN.
\n", + " לכן, אפשר להשמיט את המילה INNER מהשאילתה האחרונה, ולקבל שאילתה ששקולה לה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT m.original_title as movie_name, \n", + "\t n.name as principal_name,\n", + "\t j.name as job,\n", + "\t p.\"characters\"\n", + "FROM movies AS m\n", + " JOIN principals AS p\n", + " ON m.id = p.movie_id\n", + " JOIN jobs AS j\n", + " ON p.job_id = j.id\n", + " JOIN names AS n\n", + " ON p.name_id = n.id\n", + "WHERE m.original_title = 'The Lego Movie';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " בדומה למילת המפתח AS, רבים מחליטים דווקא להשאיר את המילה INNER לשם הַקְּרִיאוּת של השיאלתה.
\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תרגיל ביניים: זו ציפור? זה אריה? לא, זה בוקיצה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הציגו את 3 המדינות שבהן הופקו הכי הרבה סרטים מאז שנת 2000.
\n", + " לצד כל מדינה, הציגו כמה סרטים הופקו בה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## OUTER JOIN" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### הבעיה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כפי שלמדנו בתחילת ההדגמה על INNER JOIN,
\n", + " בתוצאות מוצגות רק שורות שהתנאי שכתוב ב־ON מתקיים עבורן.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כלומר, בשאילתה:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM צורות\n", + " INNER JOIN בניינים\n", + " ON בניינים.צורה = צורות.שם;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "
  1. נביט בערכים שבטבלת צורות בעמודה שם – נקרא להם \"ערכי הטבלה השמאלית\".
  2. \n", + "
  3. נביט בערכים שבטבלת בניינים בעמודה צורה – נקרא להם \"ערכי הטבלה הימנית\".
  4. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " התוצאות יכללו רק ישויות שהערך שלהם נמצא גם בערכי הטבלה השמאלית, וגם בערכי הטבלה הימנית.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שימו לב איך בתמונה הבאה הפנטגון לא נכנס לתוצאות.
\n", + " הוא נמצא בטבלה הימנית, אך הערך בעמודה שעליה עשינו ON (\"מחומש\") לא נמצא בערכי הטבלה השמאלית.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " באותה מידה, מעוין ועיגול, שהם ערכים מהטבלה השמאלית, לא נכנסו לתוצאות.
\n", + " זה המצב כיוון שאין לישויות הללו ערך תואם בטבלה הימנית.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בציור\n", + "
\n", + " ב־INNER JOIN אנחנו מוותרים על כל הישויות שלא מקיימות את השוויון שכתוב ב־ON.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### הצורך" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לפעמים נוצרים מצבים שבהם אנחנו רוצים לבצע JOIN בין טבלאות,
\n", + " אבל לא להחסיר מהתוצאות ישות שקיימת בטבלה א אם אין לה ישות תואמת בטבלה ב.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נניח שחשבנו על העניין לעומק, והגענו למסקנה שאנחנו מספיק כשרוניים כדי להחליט לבד כמה צלעות יש במחומש.
\n", + " כלומר, זה יהיה נחמד מאוד אם המידע מטבלת צורות יוצג לנו,
\n", + " אבל אם המידע בטבלת צורות חסר – נסתפק במידע מטבלת בניינים ללא המידע בטבלת צורות.
\n", + " במילים אחרות: גם אם חסר מידע משלים בטבלת צורות, נרצה בכל זאת לראות את העמודות מטבלת בניינים.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במקרה כזה, אנחנו רוצים לעשות JOIN בין טבלת בניינים לבין טבלת צורות,
\n", + " אבל לקחת את כל הישויות מטבלת בניינים, אפילו אם לבניין מסוים אין ישות תואמת בטבלת צורות.
\n", + " זה ייראה כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \"בציור\n", + "
\n", + " דוגמה לתוצאת שאילתה שלא מוותרת על אף ישות בטבלה השמאלית.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### הפתרון" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " סדר הפעולות התיאורטי יראה כך:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "
  1. בצע CROSS JOIN בין הטבלאות.
  2. \n", + "
  3. מתוך התוצאות, השאר רק את השורות שעונות על תנאי ה־ON.
  4. \n", + "
  5. אם בתוצאות, אחרי שסוננו, חסרה שורה מסוימת מהטבלה השמאלית – הכנס אותה בכל זאת.
    \n", + " בעמודות של הטבלה הימנית הזן NULL.
  6. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הרעיון של צירוף טבלאות בהן כל הישויות מטבלה אחת נמצאות בהכרח בתוצאות, נקרא OUTER JOIN.
\n", + " כשאנחנו רוצים שהטבלה השמאלית תהיה זו שממנה ילקחו הישויות בהכרח, נשתמש בפסוקית LEFT OUTER JOIN.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### תחביר" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נדגים את התחביר של שאילתת LEFT OUTER JOIN:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM בניינים\n", + " LEFT OUTER JOIN צורות\n", + " ON בניינים.צורה = צורות.שם;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " שימו לב שהטבלה שמופיעה ראשונה בשאילתה נחשבת ל\"טבלה שמאלית\" – זו שהישויות בה תמיד יופיעו בתוצאות.
\n", + " לכן, בניגוד לשאילתות שביצענו בעבר, שינינו את פסוקית ה־FROM כך שתשלוף מטבלת בניינים במקום מטבלת צורות.
\n", + " למרות שבמבט ראשון ההבדל עלול להיראות סמנטי בלבד, בפועל השינוי הזה חשוב מאוד.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### דוגמה" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " לפי מסד הנתונים שלנו, בשנת 2016 יצא הסרט בעל השם הקליט \"Bugs Bunny: Looney Tunes Cartoons 1942-1943 - Golden-Era Collection\".\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " למרבה הצער, בצעד שמזכיר את חטיף הקוקוס המפוקפק במכולת השכונתית, אף אחד לא נטל אחריות על הסרט.
\n", + " בטבלה principals לא מופיע אף לא אדם אחד שמקושר לסרט הזה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כלומר, השאילתה הבאה מחזירה לנו 0 שורות, למרות שהסרט שריר וקיים!\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies AS m\n", + " INNER JOIN principals AS p\n", + " ON p.movie_id = m.id\n", + "WHERE m.original_title = 'Bugs Bunny: Looney Tunes Cartoons 1942-1943 - Golden-Era Collection';\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"תרגול\" \n", + "
\n", + "
\n", + "

\n", + " נסו לפתור את הבעיה בעזרת OUTER JOIN.
\n", + " כתבו שאילתה שתציג את כל הסרטים, גם כאלו שלא היו בהם בעלי תפקידים.\n", + "

\n", + "
\n", + "
\n", + "

\n", + " חשוב!
\n", + " פתרו לפני שתמשיכו!\n", + "

\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " נכתוב שאילתה שבאיחוד הסרטים ובעלי התפקידים, תחזיר לנו גם סרטים בהם לא היו בעלי תפקידים:\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```sql\n", + "SELECT *\n", + "FROM movies AS m\n", + " LEFT OUTER JOIN principals AS p\n", + " ON p.movie_id = m.id;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " אם נוסיף את ה־WHERE, נראה שהפעם דווקא כן מופיע בתוצאות אותו סרט עלום מ־2016.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " \"טיפ!\"\n", + "
\n", + "
\n", + " ברוב מסדי הנתונים אין צורך לציין במפורש את המילה OUTER עבור LEFT JOIN.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## סיכום" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " במחברת זו למדנו כמה דרכים לצרף טבלאות אחת לשנייה.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הצורה הראשונה שלמדנו הייתה CROSS JOIN, שמחזירה מכפלה קרטזית בין הטבלאות.
\n", + " כלומר – עבור כל ישות בטבלה השמאלית יוצמדו כל הישויות בטבלה הימנית.
\n", + " אם הטבלה השמאלית מכילה $n$ ישויות והטבלה הימנית מכילה $m$ ישויות, בתוצאות יופיעו $n \\cdot m$ ישויות.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הצורה השנייה שלמדנו הייתה INNER JOIN, שמטרתה להחזיר ישויות תואמות בין שתי טבלאות.
\n", + " כלומר – אחרי ביצוע מכפלה קרטזית בין שתי הטבלאות, מוחזרות רק השורות שעונות על התנאי שנמצא אחרי מילת המפתח ON.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הצורה האחרונה שלמדנו הייתה OUTER JOIN, שמטרתה להחזיר את כל הישויות מטבלה מסוימת, ולצרף לכל אחת מהן מידע מטבלה נוספת – אם יש כזה.
\n", + " אפשר לדמיין את התהליך כך: אחרי ביצוע INNER JOIN בין שתי הטבלאות, \"דוחפים\" שוב לתוצאות את הישויות מהטבלה השמאלית שלא מופיעות בתוצאות.
\n", + " בעמודות בהן חסרים נתונים בעקבות החזרת הישויות מהטבלה השמאלית מציבים NULL.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## תרגילים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### יש יותר טוב מזה?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " כתבו שאילתה שמציגה את הסרט המפורסם ביותר בו שיחק שחקן מסוים.
\n", + " הניחו שהפופולריות של סרט נקבעת לפי כמות ההצבעות שקיבל.\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ארבעת המופלאים" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " הציגו את 4 השחקנים שכִכְּבו בכמות הסרטים הגדולה ביותר.
\n", + " בכמה סרטים כיכב כל אחד מהם?\n", + "

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### שיעור חופשי" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " חשבו על שאלה שמעניינת אתכם, ושניתן לענות עליה בעזרת שאילתות JOIN על הנתונים שבמסד הנתונים.
\n", + " צרו שאילתה שתעזור לכם לענות על השאלה הזו.\n", + "

" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/week13/images/clause-execution-order-2.svg b/week13/images/clause-execution-order-2.svg new file mode 100644 index 0000000..02c5c64 --- /dev/null +++ b/week13/images/clause-execution-order-2.svg @@ -0,0 +1,3 @@ + + +
SELECT *
FROM names
WHERE date_of_death IS NULL
ORDER BY children
LIMIT 5;
SELECT *...
1
1
2
2
3
3
4
4
5
5
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/clause-execution-order-3.svg b/week13/images/clause-execution-order-3.svg new file mode 100644 index 0000000..c68da76 --- /dev/null +++ b/week13/images/clause-execution-order-3.svg @@ -0,0 +1,3 @@ + + +
SELECT "year", COUNT(*) AS movie_count
FROM movies
WHERE "year" >= 1927
GROUP BY "year"
ORDER BY movie_count DESC
LIMIT 5;
SELECT "year", COUNT(*) AS movie_count...
1
1
2
2
4
4
5
5
6
6
3
3
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/clause-execution-order-4.svg b/week13/images/clause-execution-order-4.svg new file mode 100644 index 0000000..845c497 --- /dev/null +++ b/week13/images/clause-execution-order-4.svg @@ -0,0 +1,3 @@ + + +
SELECT "year", COUNT(*) AS good_movies_count
FROM movies
WHERE avg_vote > 8.0
GROUP BY "year"
HAVING COUNT(*) >= 10
ORDER BY good_movies_count DESC
LIMIT 5;
SELECT "year", COUNT(*) AS good_movies_count...
1
1
2
2
5
5
6
6
7
7
3
3
4
4
Viewer does not support full SVG 1.1
diff --git a/week13/images/clause-execution-order.svg b/week13/images/clause-execution-order.svg new file mode 100644 index 0000000..93586e7 --- /dev/null +++ b/week13/images/clause-execution-order.svg @@ -0,0 +1,3 @@ + + +
SELECT *
FROM names
WHERE height >= 200
LIMIT 10;
SELECT *...
1
1
2
2
3
3
4
4
Viewer does not support full SVG 1.1
diff --git a/week13/images/cross_join.svg b/week13/images/cross_join.svg new file mode 100644 index 0000000..4f8b0a0 --- /dev/null +++ b/week13/images/cross_join.svg @@ -0,0 +1,3 @@ + + +
   תמונה  שםמספר צלעותבנייןצורהכתובת

עיגול0עזריאלי 1משולשדרך מנחם בגין 132

עיגול0עזריאלי 2ריבועדרך מנחם בגין 132

עיגול0הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100

משולש3עזריאלי 1משולשדרך מנחם בגין 132

משולש
3עזריאלי 2ריבועדרך מנחם בגין 132

משולש3הפנטגוןמחומש
ארלינגטון, ורג'יניה, שדרות וושינגטון 100

מעוין4עזריאלי 1משולש
דרך מנחם בגין 132

מעוין4עזריאלי 2ריבוע
דרך מנחם בגין 132

מעוין
4הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100

ריבוע4עזריאלי 1משולש
דרך מנחם בגין 132

ריבוע4עזריאלי 2ריבוע
דרך מנחם בגין 132

ריבוע4הפנטגוןמחומש
ארלינגטון, ורג'יניה, שדרות וושינגטון 100
תמונה  שםמספר צלעותבנייןצורהכתובתעיגול0עזריאלי 1משולשדרך מנחם בגין 132עיגול0עזריאלי 2ריבועדרך מנחם בגין 132עיגול0הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100...
   תמונה  שםמספר צלעות
עיגול0

משולש3
מעוין4

ריבוע4
   תמונה  שםמספר צלעותעיגול0משולש3מעוין4ריבוע4
בנייןצורהכתובת
עזריאלי 1משולשדרך מנחם בגין 132
עזריאלי 2ריבועדרך מנחם בגין 132
הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
בנייןצורהכתובתעזריאלי 1משולשדרך מנחם בגין 132עזריאלי 2ריבועדרך מנחם בגין 132הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
צורות
צורות
בניינים
בניינים
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/deeper.svg b/week13/images/deeper.svg new file mode 100644 index 0000000..1b7344f --- /dev/null +++ b/week13/images/deeper.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/week13/images/deeper2.svg b/week13/images/deeper2.svg new file mode 100644 index 0000000..0c4a5e2 --- /dev/null +++ b/week13/images/deeper2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/week13/images/exercise.svg b/week13/images/exercise.svg new file mode 100644 index 0000000..21feb9f --- /dev/null +++ b/week13/images/exercise.svg @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/week13/images/inner_join.svg b/week13/images/inner_join.svg new file mode 100644 index 0000000..f828dcc --- /dev/null +++ b/week13/images/inner_join.svg @@ -0,0 +1,3 @@ + + +
   תמונה  שםמספר צלעותבנייןצורהכתובת

משולש3עזריאלי 1משולשדרך מנחם בגין 132

ריבוע4עזריאלי 2ריבוע
דרך מנחם בגין 132
תמונה  שםמספר צלעותבנייןצורהכתובתמשולש3עזריאלי 1משולשדרך מנחם בגין 132...
   תמונה  שםמספר צלעות
עיגול0

משולש3
מעוין4

ריבוע4
   תמונה  שםמספר צלעותעיגול0משולש3מעוין4ריבוע4
בנייןצורהכתובת
עזריאלי 1משולשדרך מנחם בגין 132
עזריאלי 2ריבועדרך מנחם בגין 132
הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
בנייןצורהכתובתעזריאלי 1משולשדרך מנחם בגין 132עזריאלי 2ריבועדרך מנחם בגין 132הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
צורות
צורות
בניינים
בניינים
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/inner_join2.svg b/week13/images/inner_join2.svg new file mode 100644 index 0000000..717b6eb --- /dev/null +++ b/week13/images/inner_join2.svg @@ -0,0 +1,3 @@ + + +
   תמונה  שםמספר צלעותבנייןצורהכתובת

משולש3עזריאלי 1משולשדרך מנחם בגין 132

ריבוע4עזריאלי 2ריבוע
דרך מנחם בגין 132

ריבוע4הבית של שרלוקריבוערחוב בייקר 221 ב
תמונה  שםמספר צלעותבנייןצורהכתובתמשולש3עזריאלי 1משולשדרך מנחם בגין 132...
   תמונה  שםמספר צלעות
עיגול0

משולש3
מעוין4

ריבוע4
   תמונה  שםמספר צלעותעיגול0משולש3מעוין4ריבוע4
בנייןצורהכתובת
עזריאלי 1משולשדרך מנחם בגין 132
עזריאלי 2ריבועדרך מנחם בגין 132
הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
הבית של שרלוקריבוערחוב בייקר 221 ב
בנייןצורהכתובתעזריאלי 1משולשדרך מנחם בגין 132עזריאלי 2ריבועדרך מנחם בגין 132הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100...
צורות
צורות
בניינים
בניינים
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/inner_join_intro.svg b/week13/images/inner_join_intro.svg new file mode 100644 index 0000000..5fa2751 --- /dev/null +++ b/week13/images/inner_join_intro.svg @@ -0,0 +1,3 @@ + + +
   תמונה  שםמספר צלעות
עיגול0

משולש3
מעוין4

ריבוע4
   תמונה  שםמספר צלעותעיגול0משולש3מעוין4ריבוע4
בנייןצורהכתובת
עזריאלי 1משולשדרך מנחם בגין 132
עזריאלי 2ריבועדרך מנחם בגין 132
הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
בנייןצורהכתובתעזריאלי 1משולשדרך מנחם בגין 132עזריאלי 2ריבועדרך מנחם בגין 132הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
צורות
צורות
בניינים
בניינים
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/join_intro.svg b/week13/images/join_intro.svg new file mode 100644 index 0000000..62d1231 --- /dev/null +++ b/week13/images/join_intro.svg @@ -0,0 +1,3 @@ + + +
   תמונה  שםמספר צלעות
עיגול0

משולש3
מעוין4

ריבוע4
   תמונה  שםמספר צלעותעיגול0משולש3מעוין4ריבוע4
בנייןצורהכתובת
עזריאלי 1משולשדרך מנחם בגין 132
עזריאלי 2ריבועדרך מנחם בגין 132
הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
בנייןצורהכתובתעזריאלי 1משולשדרך מנחם בגין 132עזריאלי 2ריבועדרך מנחם בגין 132הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
צורות
צורות
בניינים
בניינים
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/logo.jpg b/week13/images/logo.jpg new file mode 100644 index 0000000..5b8ca56 Binary files /dev/null and b/week13/images/logo.jpg differ diff --git a/week13/images/normalization_erd_1.svg b/week13/images/normalization_erd_1.svg new file mode 100644 index 0000000..bf1a63c --- /dev/null +++ b/week13/images/normalization_erd_1.svg @@ -0,0 +1,3 @@ + + +
movies
movies
title
title
release_year
release_year
actors
actors
text
text
int
int
text
te...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/normalization_erd_2.svg b/week13/images/normalization_erd_2.svg new file mode 100644 index 0000000..50e5bc2 --- /dev/null +++ b/week13/images/normalization_erd_2.svg @@ -0,0 +1,3 @@ + + +
movies
movies
title
title
release_year
release_year
actor_name
actor_name
actor_birth_date
actor_birth_date
text
text
int
int
text
te...
date
da...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/normalization_erd_3.svg b/week13/images/normalization_erd_3.svg new file mode 100644 index 0000000..c88c21a --- /dev/null +++ b/week13/images/normalization_erd_3.svg @@ -0,0 +1,3 @@ + + +
movies
movies
title
title
release_year
release_year
actor_name
actor_name
text
text
int
int
text
te...
actors
actors
name
name
height
height
birth_date
birth_date
text
text
float
float
date
da...
death_date
death_date
date
da...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/normalization_erd_4.svg b/week13/images/normalization_erd_4.svg new file mode 100644 index 0000000..ef579ae --- /dev/null +++ b/week13/images/normalization_erd_4.svg @@ -0,0 +1,3 @@ + + +
movies
movies
title
title
release_year
release_year
actor_name
actor_name
text
text
int
int
text
te...
actors
actors
name
name
height
height
birth_date
birth_date
text
text
float
float
date
da...
death_date
death_date
date
da...
N
N
N
N
N
N
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/normalization_erd_5.svg b/week13/images/normalization_erd_5.svg new file mode 100644 index 0000000..0fe208a --- /dev/null +++ b/week13/images/normalization_erd_5.svg @@ -0,0 +1,3 @@ + + +
movies
movies
movie_id
movie_id
title
title
release_year
release_year
actor_id
actor_id
text
text
int
int
int
int
actors
actors
actor_id
actor_id
name
name
height
height
birth_date
birth_date
death_date
death_date
text
text
float
float
date
da...
date
da...
N
N
N
N
N
N
int
int
int
int
PK
PK
PK
PK
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/normalization_erd_6.svg b/week13/images/normalization_erd_6.svg new file mode 100644 index 0000000..715b3e2 --- /dev/null +++ b/week13/images/normalization_erd_6.svg @@ -0,0 +1,3 @@ + + +
movies
movies
movie_id
movie_id
title
title
release_year
release_year
actor_id
actor_id
text
text
int
int
int
int
actors
actors
actor_id
actor_id
name
name
height
height
birth_date
birth_date
death_date
death_date
text
text
float
float
date
da...
date
da...
N
N
N
N
N
N
int
int
int
int
PK
PK
PK
PK
FK
FK
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/normalization_erd_7.svg b/week13/images/normalization_erd_7.svg new file mode 100644 index 0000000..e975f2b --- /dev/null +++ b/week13/images/normalization_erd_7.svg @@ -0,0 +1,3 @@ + + +
movies
movies
movie_id
movie_id
title
title
release_year
release_year
actor_id
actor_id
text
text
int
int
int
int
actors
actors
actor_id
actor_id
name
name
height
height
birth_date
birth_date
death_date
death_date
text
text
float
float
date
da...
date
da...
N
N
N
N
N
N
int
int
int
int
PK
PK
PK
PK
FK
FK
1
1
N
N
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/normalization_erd_8.svg b/week13/images/normalization_erd_8.svg new file mode 100644 index 0000000..27374dc --- /dev/null +++ b/week13/images/normalization_erd_8.svg @@ -0,0 +1,3 @@ + + +
movies
movies
movie_id
movie_id
title
title
release_year
release_year
actor_id
actor_id
text
text
int
int
int
int
actors
actors
actor_id
actor_id
name
name
height
height
birth_date
birth_date
death_date
death_date
text
text
float
float
date
da...
date
da...
N
N
N
N
N
N
int
int
int
int
PK
PK
PK
PK
FK
FK
1
1
N
N
languages
languages
language_id
language_id
name
name
PK
PK
text
text
int
int
language_id
language_id
int
int
FK
FK
1
1
N
N
Viewer does not support full SVG 1.1
diff --git a/week13/images/normalization_erd_9.svg b/week13/images/normalization_erd_9.svg new file mode 100644 index 0000000..86e406a --- /dev/null +++ b/week13/images/normalization_erd_9.svg @@ -0,0 +1,3 @@ + + +
movie_actors
movie_actors
movies
movies
movie_id
movie_id
title
title
release_year
release_year
text
text
int
int
actors
actors
actor_id
actor_id
name
name
height
height
birth_date
birth_date
death_date
death_date
text
text
float
float
date
da...
date
da...
N
N
N
N
N
N
int
int
int
int
PK
PK
PK
PK
1
1
N
N
actor_id
actor_id
int
int
FK
FK
movie_id
movie_id
int
int
FK
FK
1
1
N
N
Viewer does not support full SVG 1.1
diff --git a/week13/images/outer_join.svg b/week13/images/outer_join.svg new file mode 100644 index 0000000..001f15b --- /dev/null +++ b/week13/images/outer_join.svg @@ -0,0 +1,3 @@ + + +
בנייןצורהכתובתתמונהשםמספר צלעות
עזריאלי 1משולשדרך מנחם בגין 132
משולש3
עזריאלי 2ריבועדרך מנחם בגין 132
ריבוע
4
הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100NULLNULLNULL
הבית של שרלוקריבוע
רחוב בייקר 221 ב
ריבוע4
בנייןצורהכתובתתמונהשםמספר צלעותעזריאלי 1משולשדרך מנחם בגין 132משולש3עזריאלי 2ריבועדרך מנחם בגין 132ריבוע...
   תמונה  שםמספר צלעות
עיגול0

משולש3
מעוין4

ריבוע4
   תמונה  שםמספר צלעותעיגול0משולש3מעוין4ריבוע4
בנייןצורהכתובת
עזריאלי 1משולשדרך מנחם בגין 132
עזריאלי 2ריבועדרך מנחם בגין 132
הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100
הבית של שרלוקריבוערחוב בייקר 221 ב
בנייןצורהכתובתעזריאלי 1משולשדרך מנחם בגין 132עזריאלי 2ריבועדרך מנחם בגין 132הפנטגוןמחומשארלינגטון, ורג'יניה, שדרות וושינגטון 100...
צורות
צורות
בניינים
בניינים
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/week13/images/recall.svg b/week13/images/recall.svg new file mode 100644 index 0000000..c1b6d3f --- /dev/null +++ b/week13/images/recall.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/week13/images/schema_diagram.png b/week13/images/schema_diagram.png new file mode 100644 index 0000000..38213be Binary files /dev/null and b/week13/images/schema_diagram.png differ diff --git a/week13/images/table1.svg b/week13/images/table1.svg new file mode 100644 index 0000000..e01cadd --- /dev/null +++ b/week13/images/table1.svg @@ -0,0 +1,3 @@ + + +
idcustomer_nameorder_datedrink_namedrink_price
1Ronit Feldman2019-11-09Crazy Earl57
2Yam Mesicka2019-07-22Black Magic Julep63
3Dave Arnold1933-12-05Thai Basil Daiquiri
59
4Racky Ducky2019-09-06Jungle Bird66
idcustomer_nameorder_datedrink_namedrink_price1Ronit Feldman2019-11-09Crazy Earl572Yam Mesicka2019-07-22Black Magic Julep633Dave Arnold1933-12-05Thai Basil...
משקאות שנמכרו באורנוס (drinks_sold)
משקאות שנמכרו באורנוס (drinks_sold)
רשומה / ישות
רשומה / ישות
עמודה / תכונה
עמודה / תכונה
שמות העמודות
שמות העמודות
Viewer does not support full SVG 1.1
diff --git a/week13/images/table_db1.png b/week13/images/table_db1.png new file mode 100644 index 0000000..07a3b57 Binary files /dev/null and b/week13/images/table_db1.png differ diff --git a/week13/images/tip.png b/week13/images/tip.png new file mode 100644 index 0000000..a563b03 Binary files /dev/null and b/week13/images/tip.png differ diff --git a/week13/images/warning.png b/week13/images/warning.png new file mode 100644 index 0000000..4863041 Binary files /dev/null and b/week13/images/warning.png differ diff --git a/week13/resources/Create DB.ipynb b/week13/resources/Create DB.ipynb new file mode 100644 index 0000000..ddebabf --- /dev/null +++ b/week13/resources/Create DB.ipynb @@ -0,0 +1,328 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# DB from https://www.kaggle.com/stefanoleone992/imdb-extensive-dataset\n", + "# By Stefano Leone - https://www.kaggle.com/stefanoleone992\n", + "# Published under CC0 license." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import gzip\n", + "import os\n", + "import pathlib\n", + "import sqlite3\n", + "from typing import Optional, Tuple\n", + "\n", + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sqlalchemy import (\n", + " Column, DateTime, Float, ForeignKey,\n", + " Integer, String, Table, create_engine,\n", + ")\n", + "from sqlalchemy.ext.declarative import declarative_base\n", + "\n", + "DB_PATH = 'imdb3.db'\n", + "\n", + "engine = create_engine(f'sqlite:///{DB_PATH}', echo=True)\n", + "Base = declarative_base()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Country(Base):\n", + " __tablename__ = 'countries'\n", + "\n", + " id = Column(Integer, primary_key=True)\n", + " name = Column(String, nullable=False)\n", + "\n", + "\n", + "class Genre(Base):\n", + " __tablename__ = 'genres'\n", + "\n", + " id = Column(Integer, primary_key=True)\n", + " name = Column(String, nullable=False)\n", + "\n", + "\n", + "class Language(Base):\n", + " __tablename__ = 'languages'\n", + "\n", + " id = Column(Integer, primary_key=True)\n", + " name = Column(String, nullable=False)\n", + "\n", + "\n", + "movie_country = Table('movie_countries', Base.metadata,\n", + " Column('movie_id', String, ForeignKey('movies.id')),\n", + " Column('country_id', Integer, ForeignKey('countries.id'))\n", + ")\n", + "\n", + "\n", + "movie_genre = Table('movie_genres', Base.metadata,\n", + " Column('movie_id', String, ForeignKey('movies.id')),\n", + " Column('genre_id', Integer, ForeignKey('genres.id'))\n", + ")\n", + "\n", + "\n", + "movie_language = Table('movie_languages', Base.metadata,\n", + " Column('movie_id', String, ForeignKey('movies.id')),\n", + " Column('language_id', Integer, ForeignKey('languages.id'))\n", + ")\n", + "\n", + " \n", + "class Movie(Base):\n", + " __tablename__ = 'movies'\n", + "\n", + " id = Column(String, primary_key=True)\n", + " title = Column(String, nullable=False)\n", + " original_title = Column(String, nullable=False)\n", + " year = Column(Integer, nullable=False)\n", + " avg_vote = Column(String, nullable=False)\n", + " votes = Column(Integer, nullable=False)\n", + " duration = Column(Integer, nullable=False)\n", + " budget = Column(String)\n", + " gross_income = Column(String)\n", + "\n", + " \n", + "class Name(Base):\n", + " __tablename__ = 'names'\n", + "\n", + " id = Column(String, primary_key=True)\n", + " name = Column(String, nullable=False)\n", + " height = Column(Float)\n", + " date_of_birth = Column(DateTime, nullable=True)\n", + " date_of_death = Column(DateTime, nullable=True)\n", + " children = Column(Integer, nullable=False)\n", + "\n", + "\n", + "class Principal(Base):\n", + " __tablename__ = 'principals'\n", + "\n", + " movie_id = Column(String, ForeignKey('movies.id'), primary_key=True)\n", + " ordering = Column(String, primary_key=True)\n", + " name_id = Column(String, ForeignKey('names.id'), nullable=False)\n", + " job_id = Column(Integer, ForeignKey('jobs.id'), nullable=False)\n", + " characters = Column(String)\n", + "\n", + "\n", + "class Job(Base):\n", + " __tablename__ = 'jobs'\n", + "\n", + " id = Column(Integer, primary_key=True)\n", + " name = Column(String, nullable=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Base.metadata.create_all(engine)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "CREATE_TABLE_SETTINGS = {'con': engine, 'if_exists': 'append'}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "names = pd.read_csv(\n", + " 'names.csv',\n", + " parse_dates=['date_of_birth', 'date_of_death'],\n", + " infer_datetime_format=True,\n", + " na_values=\"None\",\n", + ")[\n", + " ['imdb_name_id', 'name', 'height',\n", + " 'date_of_birth', 'date_of_death', 'children']\n", + "]\n", + "\n", + "principals = pd.read_csv(\n", + " 'title_principals.csv', na_values=\"None\",\n", + ")[['imdb_title_id', 'ordering', 'imdb_name_id', 'category', 'characters']]\n", + "\n", + "movies = pd.read_csv(\n", + " 'movies.csv', parse_dates=['date_published'], na_values=\"None\",\n", + ")[\n", + " ['imdb_title_id', 'title', 'original_title', 'year', 'genre',\n", + " 'duration', 'avg_vote', 'votes', 'country', 'language', 'budget',\n", + " 'worlwide_gross_income']\n", + "]\n", + "\n", + "\n", + "movies.rename(\n", + " columns={'imdb_title_id': 'id', 'worlwide_gross_income': 'gross_income'},\n", + " inplace=True,\n", + ")\n", + "principals.rename(\n", + " columns={'imdb_title_id': 'movie_id', 'imdb_name_id': 'name_id',\n", + " 'category': 'job_id'},\n", + " inplace=True,\n", + ")\n", + "names.rename(columns={'imdb_name_id': 'id'}, inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "names['date_of_birth'] = pd.to_datetime(names['date_of_birth'], errors='coerce')\n", + "names['date_of_death'] = pd.to_datetime(names['date_of_death'], errors='coerce')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "movies.set_index('id', inplace=True)\n", + "names.set_index('id', inplace=True)\n", + "principals.set_index(['movie_id', 'ordering'], inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tables = {\n", + " 'names': names,\n", + " 'principals': principals,\n", + " 'movies': movies,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create M2Ms\n", + "\n", + "TO_CONVERT = [\n", + " ('movies', 'genre', 'genres'),\n", + " ('movies', 'language', 'languages'),\n", + " ('movies', 'country', 'countries'),\n", + "]\n", + "\n", + "for t, c, many in TO_CONVERT:\n", + " values = {\n", + " value.strip()\n", + " for vals in tables[t][c].str.split(',')\n", + " for value in (vals if isinstance(vals, list) else [])\n", + " }\n", + "\n", + " second_table = list(enumerate(values, 1))\n", + " tables[many] = pd.DataFrame(second_table, columns=['id', 'name'])\n", + " tables[many].set_index('id', inplace=True)\n", + "\n", + " # Create the M2M relationships\n", + " values_kv = {v: k for k, v in second_table}\n", + " titles_value = (\n", + " (i, values_kv.get(value))\n", + " for i, title in tables[t].iterrows() if isinstance(title[c], str)\n", + " for value in map(str.strip, title[c].split(','))\n", + " )\n", + "\n", + " tables[f'movie_{many}'] = pd.DataFrame(titles_value, columns=['movie_id', f'{c}_id'])\n", + " tables[t].drop([c], axis=1, inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "jobs = list(enumerate(principals['job_id'].unique(), 1))\n", + "tables['jobs'] = pd.DataFrame(jobs, columns=['id', 'name'])\n", + "for job_id, job_name in jobs:\n", + " tables['principals'].replace({job_name: job_id}, inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "delete_chars = str.maketrans(\"\", \"\", \"[]\\\"\")\n", + "\n", + "tables['principals']['characters'] = (\n", + " tables['principals']['characters'].astype(str)\n", + " .str.translate(delete_chars)\n", + " .replace(',', ', ').replace('nan', np.nan)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "WITHOUT_INDEX = {'movie_genres', 'movie_languages', 'movie_countries', 'jobs'}\n", + "\n", + "for name, df in tables.items():\n", + " CREATE_TABLE_SETTINGS['index'] = name not in WITHOUT_INDEX\n", + " print(name, CREATE_TABLE_SETTINGS)\n", + " df.to_sql(name, **CREATE_TABLE_SETTINGS)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/week13/resources/cocktails.csv b/week13/resources/cocktails.csv new file mode 100644 index 0000000..1a706b5 --- /dev/null +++ b/week13/resources/cocktails.csv @@ -0,0 +1,1001 @@ +id,full_name,date_ordered,drink_name,price +1,Ronit Feldman,2020-10-15 21:06,Eclipse,66 +2,Garrot Ancell,2020-10-15 20:54,Truffle Sazerac,64 +3,Dara Urridge,2020-10-15 22:38,Eclipse,66 +4,Ritchie McCormack,2020-10-15 21:10,Eclipse,66 +5,Gavra Thurby,2020-10-15 21:00,Irish Coffee,54 +6,Kimberli Tranfield,2020-10-15 23:08,Irish Coffee,54 +7,Hart Genny,2020-10-15 23:12,Eclipse,66 +8,Sharl O'Feeny,2020-10-15 20:48,Eclipse,66 +9,Yalonda Hartford,2020-10-15 21:02,Bloody Mary,57 +10,Courtney Gocke,2020-10-15 20:26,Eclipse,66 +11,Poul Sparham,2020-10-15 19:59,Eclipse,66 +12,Yam Mesicka,2020-10-15 20:20,Eclipse,66 +13,Virgie Blaschke,2020-10-15 22:14,Irish Coffee,54 +14,Ronit Feldman,2020-10-15 20:53,Daiquiri,54 +15,Abner Smedley,2020-10-15 18:20,Eclipse,66 +16,Dorri Leonarde,2020-10-16 21:21,Eclipse,66 +17,Maryanna Brisset,2020-10-15 18:48,Chartreuse Cobbler,61 +18,Teodor Gilluley,2020-10-15 21:00,Eclipse,66 +19,Ezekiel Mugridge,2020-10-15 21:40,Eclipse,66 +20,Konrad Ishaki,2020-10-15 20:39,Truffle Sazerac,64 +21,Khalil Umfrey,2020-10-15 20:58,Bloody Mary,57 +22,Rainer Stocker,2020-10-15 20:24,Eclipse,66 +23,Lucius Chasemore,2020-10-15 20:32,Eclipse,66 +24,Jock Dudeney,2020-10-15 21:09,Eclipse,66 +25,Romy MacTerrelly,2020-10-15 21:47,Irish Coffee,54 +26,Ebeneser Burgiss,2020-10-15 19:10,Eclipse,66 +27,Atlanta Bahls,2020-10-14 23:54,Eclipse,66 +28,Sheelah Fearne,2020-10-16 22:43,Eclipse,66 +29,Pandora Morrison,2020-10-15 21:24,Eclipse,66 +30,Ronit Feldman,2020-10-15 21:47,Daiquiri,54 +31,Ronit Feldman,2020-10-16 18:34,Daiquiri,54 +32,Jackie Farreil,2020-10-15 20:53,Eclipse,66 +33,Buiron Fann,2020-10-16 19:04,Eclipse,66 +34,Adolf Lung,2020-10-15 22:08,Eclipse,66 +35,Arnold Catonnet,2020-10-15 22:55,Eclipse,66 +36,Ronit Feldman,2020-10-15 22:28,Irish Coffee,54 +37,Rasia Greene,2020-10-15 21:05,Eclipse,66 +38,Alexi Cavil,2020-10-15 18:37,Eclipse,66 +39,Ronit Feldman,2020-10-15 21:00,Daiquiri,54 +40,Zebulon Overington,2020-10-15 19:30,Eclipse,66 +41,Raffaello Bittleson,2020-10-15 18:00,Eclipse,66 +42,Dani Hollidge,2020-10-15 21:20,Eclipse,66 +43,Jocko Cunney,2020-10-15 20:10,Eclipse,66 +44,Ronit Feldman,2020-10-15 20:54,Eclipse,66 +45,Lane Tatford,2020-10-15 21:32,Chartreuse Cobbler,61 +46,Davina MacGorrie,2020-10-15 22:53,Black Magic Julep,69 +47,Jocelyn Aglione,2020-10-15 22:11,Eclipse,66 +48,Kassie Garnall,2020-10-15 20:09,Eclipse,66 +49,Lorraine Schaffel,2020-10-15 23:00,Eclipse,66 +50,Vick Pond,2020-10-15 22:28,Eclipse,66 +51,Major Bautiste,2020-10-15 20:10,Eclipse,66 +52,Francesco Cansdale,2020-10-15 20:42,Eclipse,66 +53,Yam Mesicka,2020-10-15 23:18,Eclipse,66 +54,Addy Jewson,2020-10-15 20:50,Eclipse,66 +55,Yam Mesicka,2020-10-15 19:11,Eclipse,66 +56,Ronit Feldman,2020-10-15 18:41,Daiquiri,54 +57,Revkah Judge,2020-10-15 21:12,Bloody Mary,57 +58,Gena Skeffington,2020-10-15 20:19,Eclipse,66 +59,Donelle Lucien,2020-10-15 19:28,Eclipse,66 +60,Whitman Vondracek,2020-10-15 22:54,Bloody Mary,57 +61,Der Pepi,2020-10-16 18:53,Eclipse,66 +62,Mitch Mulroy,2020-10-15 18:06,Chartreuse Cobbler,61 +63,Karleen Boullen,2020-10-15 22:03,Black Magic Julep,69 +64,Tobit Ridout,2020-10-15 20:42,Bloody Mary,57 +65,Gonzales Northin,2020-10-15 18:22,Eclipse,66 +66,Hildagarde Zanni,2020-10-15 22:18,Bloody Mary,57 +67,Meredeth Suarez,2020-10-15 21:25,Bloody Mary,57 +68,Gwenni Slaney,2020-10-15 20:07,Chartreuse Cobbler,61 +69,Lissy Trenouth,2020-10-14 22:36,Eclipse,66 +70,Martguerita Melanaphy,2020-10-15 19:01,Black Magic Julep,69 +71,Garey Rosellini,2020-10-15 21:08,Eclipse,66 +72,Cathe Freak,2020-10-15 21:12,Eclipse,66 +73,Ramonda Etherson,2020-10-15 21:32,Black Magic Julep,69 +74,Lanie Meharg,2020-10-15 21:46,Eclipse,66 +75,Ronit Feldman,2020-10-15 20:07,Daiquiri,54 +76,Lesley Bollum,2020-10-15 20:33,Eclipse,66 +77,Brian Berre,2020-10-15 21:20,Irish Coffee,54 +78,Mada Kayser,2020-10-15 22:37,Eclipse,66 +79,Fayette Aslott,2020-10-15 20:41,Bloody Mary,57 +80,Alasdair St. Clair,2020-10-15 19:07,Eclipse,66 +81,Audry Gail,2020-10-15 21:13,Eclipse,66 +82,Madison Ponting,2020-10-15 20:54,Eclipse,66 +83,Deedee Morrallee,2020-10-15 19:19,Truffle Sazerac,64 +84,Zenia Sackes,2020-10-15 20:39,Eclipse,66 +85,Gussie Whitebread,2020-10-15 18:31,Bloody Mary,57 +86,Domenico Boutflour,2020-10-15 20:32,Daiquiri,54 +87,Clea Camidge,2020-10-15 21:20,Eclipse,66 +88,Kenyon Martini,2020-10-15 21:24,Eclipse,66 +89,Yam Mesicka,2020-10-15 21:14,Eclipse,66 +90,Ivie Pedrol,2020-10-15 20:34,Daiquiri,54 +91,Ronit Feldman,2020-10-15 21:13,Irish Coffee,54 +92,Mic Gogie,2020-10-15 20:33,Truffle Sazerac,64 +93,Dasi Piddle,2020-10-16 18:50,Bloody Mary,57 +94,Glenden Duffer,2020-10-15 22:01,Eclipse,66 +95,Sherri Silverson,2020-10-15 23:24,Black Magic Julep,69 +96,Odele Motte,2020-10-14 19:39,Eclipse,66 +97,Yam Mesicka,2020-10-15 21:38,Eclipse,66 +98,Ema Daverin,2020-10-15 21:22,Eclipse,66 +99,Robyn Waring,2020-10-15 21:15,Eclipse,66 +100,Charin Thowes,2020-10-15 20:00,Bloody Mary,57 +101,Rickard Murkus,2020-10-15 20:52,Eclipse,66 +102,Dom Stranaghan,2020-10-15 21:10,Eclipse,66 +103,Yam Mesicka,2020-10-15 18:09,Eclipse,66 +104,Panchito Doorbar,2020-10-15 20:00,Truffle Sazerac,64 +105,Abbey Kochs,2020-10-15 21:07,Eclipse,66 +106,Robbie Sylvaine,2020-10-15 22:58,Bloody Mary,57 +107,Catlee Barrand,2020-10-15 21:55,Eclipse,66 +108,Marie Brothwell,2020-10-15 21:29,Chartreuse Cobbler,61 +109,Matt McClements,2020-10-15 23:40,Black Magic Julep,69 +110,Lavena Lackeye,2020-10-15 20:32,Chartreuse Cobbler,61 +111,Ram Lingwood,2020-10-15 23:44,Eclipse,66 +112,Colby Sinson,2020-10-15 23:13,Black Magic Julep,69 +113,Berte Simonnet,2020-10-15 20:28,Eclipse,66 +114,Logan Espadas,2020-10-15 19:07,Eclipse,66 +115,Scarlet Mosey,2020-10-14 23:13,Eclipse,66 +116,Krissy Oglethorpe,2020-10-15 18:07,Black Magic Julep,69 +117,Rikki Beglin,2020-10-15 21:58,Eclipse,66 +118,Sarena Marsden,2020-10-15 20:13,Eclipse,66 +119,Ingaborg Maddern,2020-10-15 22:32,Eclipse,66 +120,Cointon Jancso,2020-10-15 20:39,Eclipse,66 +121,Iolanthe Siddeley,2020-10-15 20:47,Chartreuse Cobbler,61 +122,Dorise Dabinett,2020-10-15 21:17,Eclipse,66 +123,Desiri Pea,2020-10-15 18:23,Bloody Mary,57 +124,Ronit Feldman,2020-10-15 22:59,Daiquiri,54 +125,Lorne Earpe,2020-10-15 21:04,Eclipse,66 +126,Nefen Restill,2020-10-13 23:54,Eclipse,66 +127,Erma Lidgate,2020-10-15 21:42,Irish Coffee,54 +128,Ashla Gaytor,2020-10-15 21:36,Bloody Mary,57 +129,Linzy Campos,2020-10-13 19:51,Irish Coffee,54 +130,Yam Mesicka,2020-10-15 19:32,Black Magic Julep,69 +131,Melita Alster,2020-10-15 19:14,Eclipse,66 +132,Yam Mesicka,2020-10-15 20:51,Eclipse,66 +133,Lenette Mourton,2020-10-14 22:10,Black Magic Julep,69 +134,Dolly Glassopp,2020-10-15 20:20,Chartreuse Cobbler,61 +135,Shelagh Le Gassick,2020-10-15 19:47,Irish Coffee,54 +136,Margarita Faircliffe,2020-10-14 22:43,Eclipse,66 +137,Simona Peckham,2020-10-15 23:02,Irish Coffee,54 +138,Barnabas Le Teve,2020-10-14 22:41,Eclipse,66 +139,Dierdre Baack,2020-10-14 22:10,Chartreuse Cobbler,61 +140,Gallard Kightly,2020-10-15 20:16,Eclipse,66 +141,Ulrich Paine,2020-10-15 19:42,Eclipse,66 +142,Desi Spanswick,2020-10-14 23:57,Black Magic Julep,69 +143,Horatia Patinkin,2020-10-15 23:43,Irish Coffee,54 +144,Jeremiah Wolfarth,2020-10-15 19:40,Eclipse,66 +145,Kelcie Hollidge,2020-10-15 21:38,Chartreuse Cobbler,61 +146,Otes Maraga,2020-10-15 21:45,Chartreuse Cobbler,61 +147,Felice Slimme,2020-10-15 21:19,Irish Coffee,54 +148,Langston Prescot,2020-10-15 23:46,Eclipse,66 +149,Rutledge Abbett,2020-10-15 21:17,Eclipse,66 +150,Alica Inderwick,2020-10-15 19:57,Eclipse,66 +151,Sigfried Gleeton,2020-10-15 19:30,Eclipse,66 +152,Heinrick Gillard,2020-10-15 21:18,Eclipse,66 +153,Donni Pym,2020-10-15 22:47,Eclipse,66 +154,Gerrilee Cottingham,2020-10-15 18:27,Eclipse,66 +155,Eamon Lucia,2020-10-15 20:36,Eclipse,66 +156,Crissy Duley,2020-10-15 18:05,Eclipse,66 +157,Lexy Faltin,2020-10-15 22:01,Eclipse,66 +158,Emmi Dando,2020-10-15 18:49,Eclipse,66 +159,Fax Board,2020-10-15 20:38,Truffle Sazerac,64 +160,Claudina Fickling,2020-10-15 21:20,Chartreuse Cobbler,61 +161,Ilaire Rugieri,2020-10-13 23:05,Bloody Mary,57 +162,Sianna Wanne,2020-10-15 21:15,Eclipse,66 +163,Dorry Longhorn,2020-10-15 23:17,Bloody Mary,57 +164,Yam Mesicka,2020-10-15 22:22,Eclipse,66 +165,Ronit Feldman,2020-10-16 18:02,Eclipse,66 +166,Meg Hourahan,2020-10-15 21:00,Irish Coffee,54 +167,Denys Cranefield,2020-10-14 22:43,Eclipse,66 +168,Ronit Feldman,2020-10-15 23:17,Daiquiri,54 +169,Kin Darwood,2020-10-15 22:48,Chartreuse Cobbler,61 +170,Barbabas Paskins,2020-10-15 21:02,Eclipse,66 +171,Yam Mesicka,2020-10-16 23:46,Black Magic Julep,69 +172,Goddart McGonnell,ERROR RECORDING DATE,Bloody Mary,57 +173,Avie Stentiford,2020-10-15 19:29,Eclipse,66 +174,Elberta Hickenbottom,2020-10-15 20:11,Chartreuse Cobbler,61 +175,Evanne Waddingham,2020-10-15 19:01,Chartreuse Cobbler,61 +176,Corney Duplain,2020-10-15 19:47,Bloody Mary,57 +177,Rosa Gravener,2020-10-15 21:22,Eclipse,66 +178,Rafaellle Smithe,2020-10-15 19:34,Eclipse,66 +179,Jehu Divers,2020-10-15 21:30,Black Magic Julep,69 +180,Hanson Mortlock,2020-10-15 18:51,Truffle Sazerac,64 +181,Ronit Feldman,2020-10-15 19:18,Eclipse,66 +182,Yam Mesicka,2020-10-15 19:33,Eclipse,66 +183,Theda Scadding,2020-10-15 19:35,Eclipse,66 +184,Kathryne Guilaem,2020-10-15 23:52,Irish Coffee,54 +185,Midge Sich,2020-10-15 20:54,Truffle Sazerac,64 +186,Bernita Amys,2020-10-15 21:07,Eclipse,66 +187,Ronit Feldman,2020-10-15 20:44,Bloody Mary,57 +188,Yam Mesicka,2020-10-15 22:54,Eclipse,66 +189,Edi Bedrosian,2020-10-15 20:09,Eclipse,66 +190,Bruno Silbersak,2020-10-15 21:28,Eclipse,66 +191,Dorette Fasham,2020-10-15 19:02,Chartreuse Cobbler,61 +192,Puff Dreng,2020-10-16 18:15,Eclipse,66 +193,Elmo Humber,2020-10-15 18:38,Eclipse,66 +194,Yam Mesicka,2020-10-15 19:33,Eclipse,66 +195,Gladi Bover,2020-10-15 23:12,Eclipse,66 +196,Chalmers Dempsey,2020-10-15 22:30,Daiquiri,54 +197,Fidel Sawer,2020-10-15 21:31,Eclipse,66 +198,Kala Suggate,2020-10-15 20:21,Eclipse,66 +199,Cedric Castro,2020-10-15 22:35,Eclipse,66 +200,Ronit Feldman,2020-10-15 20:01,Eclipse,66 +201,Piper Urry,2020-10-16 19:09,Eclipse,66 +202,Ches Caseborne,2020-10-15 18:02,Eclipse,66 +203,Susie Presidey,2020-10-15 20:33,Chartreuse Cobbler,61 +204,Joanna Dorran,2020-10-16 19:11,Eclipse,66 +205,Dolli Mithon,2020-10-16 19:37,Daiquiri,54 +206,Callie Iddens,2020-10-15 20:15,Daiquiri,54 +207,Garry Schoolfield,2020-10-14 21:05,Truffle Sazerac,64 +208,Calida Dower,2020-10-15 18:17,Eclipse,66 +209,Linda Presley,2020-10-15 20:29,Eclipse,66 +210,Maximilien Moggan,2020-10-15 20:15,Irish Coffee,54 +211,Kelcy Hardiker,2020-10-15 22:23,Chartreuse Cobbler,61 +212,Devlin Yandle,2020-10-14 22:33,Black Magic Julep,69 +213,Yule Ledson,2020-10-15 18:31,Daiquiri,54 +214,Avrit Umpleby,2020-10-15 21:41,Eclipse,66 +215,Yam Mesicka,2020-10-14 22:40,Black Magic Julep,69 +216,Orren Lodwig,2020-10-15 19:53,Irish Coffee,54 +217,Reidar Farreil,2020-10-15 20:20,Chartreuse Cobbler,61 +218,Xymenes Hacquard,2020-10-15 19:08,Eclipse,66 +219,Colin Swansbury,2020-10-15 20:02,Chartreuse Cobbler,61 +220,Derrik Powter,2020-10-15 22:46,Bloody Mary,57 +221,Hale Edger,2020-10-15 19:35,Black Magic Julep,69 +222,Horatius Furlonge,2020-10-15 19:20,Bloody Mary,57 +223,Darnall De Moreno,2020-10-15 21:20,Eclipse,66 +224,Keane Brosel,2020-10-14 23:03,Eclipse,66 +225,Jorry Gay,2020-10-15 20:51,Eclipse,66 +226,Thoma Rawles,2020-10-14 22:51,Eclipse,66 +227,Leonie Yearron,2020-10-15 21:52,Eclipse,66 +228,Corena Ingold,2020-10-15 19:31,Eclipse,66 +229,Lavina Springle,2020-10-16 21:07,Eclipse,66 +230,Erna Phillippo,2020-10-15 21:08,Eclipse,66 +231,Glenn de Marco,2020-10-15 20:12,Irish Coffee,54 +232,Miof mela Pye,2020-10-15 22:29,Eclipse,66 +233,Dasie Tybalt,2020-10-15 22:39,Eclipse,66 +234,Gardener Spatarul,2020-10-16 18:35,Bloody Mary,57 +235,Pamella Shedden,2020-10-14 20:28,Black Magic Julep,69 +236,Arv Dilleway,2020-10-15 20:26,Daiquiri,54 +237,Ronit Feldman,2020-10-15 22:23,Eclipse,66 +238,Kleon Fagan,2020-10-15 19:33,Eclipse,66 +239,Chaddy Daout,2020-10-15 21:25,Irish Coffee,54 +240,Marillin Sinnocke,2020-10-15 21:55,Eclipse,66 +241,Gran Frise,2020-10-15 23:05,Black Magic Julep,69 +242,Ronit Feldman,2020-10-15 20:21,Eclipse,66 +243,Nedda Manuele,2020-10-15 20:04,Eclipse,66 +244,Yam Mesicka,2020-10-14 23:45,Eclipse,66 +245,Noel Beaze,2020-10-15 21:39,Eclipse,66 +246,Yam Mesicka,2020-10-14 22:15,Eclipse,66 +247,Eloise Baser,2020-10-15 19:29,Eclipse,66 +248,Gilly Le Marchand,2020-10-15 21:04,Eclipse,66 +249,Rem Andryushin,2020-10-15 23:13,Chartreuse Cobbler,61 +250,Merralee Brearty,2020-10-15 19:48,Eclipse,66 +251,Julienne Haggleton,2020-10-15 23:05,Eclipse,66 +252,Kalinda Raithby,2020-10-15 20:35,Eclipse,66 +253,Ronit Feldman,2020-10-14 23:16,Truffle Sazerac,64 +254,Yasmin Perrett,2020-10-15 21:43,Eclipse,66 +255,Dorree Dell Casa,2020-10-15 19:25,Eclipse,66 +256,Orsola Di Filippo,2020-10-16 18:59,Eclipse,66 +257,Celinka Bevir,2020-10-15 23:43,Irish Coffee,54 +258,Elfrieda Forbear,2020-10-15 20:34,Black Magic Julep,69 +259,Mahala Mapham,2020-10-15 19:51,Chartreuse Cobbler,61 +260,Sybil Wollrauch,2020-10-15 21:02,Irish Coffee,54 +261,Jacquelyn Fortin,2020-10-15 20:56,Eclipse,66 +262,Davon Haggard,2020-10-14 21:17,Chartreuse Cobbler,61 +263,Ronit Feldman,2020-10-15 19:20,Eclipse,66 +264,Edlin Lemanu,2020-10-15 22:38,Irish Coffee,54 +265,Bear Logsdale,2020-10-15 21:09,Truffle Sazerac,64 +266,Farrand Kiendl,2020-10-15 18:08,Eclipse,66 +267,Odo McFarlane,2020-10-15 20:47,Eclipse,66 +268,Uriel Matuszak,2020-10-15 20:56,Eclipse,66 +269,Pia Olfert,2020-10-15 20:32,Chartreuse Cobbler,61 +270,Dun Fazakerley,2020-10-15 21:45,Eclipse,66 +271,Wanids Jenkyn,2020-10-15 20:50,Eclipse,66 +272,Marcellina Kirsop,2020-10-14 22:27,Black Magic Julep,69 +273,Ariadne Guilford,2020-10-15 22:35,Eclipse,66 +274,Den Thurnham,2020-10-15 20:46,Bloody Mary,57 +275,Trev Soffe,2020-10-15 20:20,Eclipse,66 +276,Sharity Latus,2020-10-15 22:27,Eclipse,66 +277,Caro Connikie,2020-10-15 21:47,Bloody Mary,57 +278,Florida Razoux,2020-10-15 20:26,Eclipse,66 +279,Betteann Jantel,2020-10-15 20:58,Eclipse,66 +280,Bondy Burgett,2020-10-15 18:18,Daiquiri,54 +281,Findlay Domico,2020-10-15 18:28,Irish Coffee,54 +282,Andy Drake,2020-10-15 20:46,Eclipse,66 +283,Vivie Treske,2020-10-15 22:56,Eclipse,66 +284,Robinett Louder,2020-10-15 22:12,Chartreuse Cobbler,61 +285,Ginny Di Maria,2020-10-14 23:17,Bloody Mary,57 +286,Robbyn Mockler,2020-10-15 20:36,Eclipse,66 +287,Yam Mesicka,2020-10-15 22:22,Eclipse,66 +288,Karoly Dellenbach,2020-10-15 22:49,Eclipse,66 +289,Caldwell McCrann,2020-10-15 20:08,Eclipse,66 +290,Zelma Steagall,2020-10-15 20:47,Irish Coffee,54 +291,Micheline Gilman,2020-10-15 20:59,Black Magic Julep,69 +292,Linell Feechan,2020-10-17 19:14,Irish Coffee,54 +293,Virgil Gillbe,2020-10-15 21:40,Truffle Sazerac,64 +294,Prentiss Milliken,2020-10-15 19:55,Eclipse,66 +295,Ianthe Prue,2020-10-15 21:29,Chartreuse Cobbler,61 +296,Clayborn Jasiak,2020-10-15 19:17,Eclipse,66 +297,Matthiew Dunham,2020-10-15 20:01,Eclipse,66 +298,Ethelin Dimmick,2020-10-15 20:47,Daiquiri,54 +299,Alverta O'Keevan,2020-10-15 22:39,Bloody Mary,57 +300,Taber Swatheridge,2020-10-15 20:31,Daiquiri,54 +301,Thain McWhinnie,2020-10-15 20:20,Irish Coffee,54 +302,Kathi Bevan,2020-10-16 18:18,Bloody Mary,57 +303,Bailie Selvey,2020-10-15 22:47,Eclipse,66 +304,Nettle Jaqueme,2020-10-15 21:17,Irish Coffee,54 +305,Yam Mesicka,2020-10-15 21:15,Black Magic Julep,69 +306,Gerrie Elsmere,2020-10-15 18:02,Eclipse,66 +307,Skell Sive,2020-10-15 21:36,Truffle Sazerac,64 +308,Hersh Exposito,2020-10-15 18:18,Daiquiri,54 +309,Joel Dami,2020-10-15 19:48,Daiquiri,54 +310,Vanya Biasioni,2020-10-14 22:02,Bloody Mary,57 +311,Aggy Druce,2020-10-15 23:55,Chartreuse Cobbler,61 +312,Ugo McBeth,2020-10-15 20:15,Eclipse,66 +313,Raynell Cutting,2020-10-15 20:22,Black Magic Julep,69 +314,Truda Meachen,2020-10-14 22:21,Eclipse,66 +315,Anders Schutte,2020-10-14 23:17,Eclipse,66 +316,Yam Mesicka,2020-10-15 18:52,Eclipse,66 +317,Trixie Quennell,2020-10-15 21:45,Eclipse,66 +318,Ronit Feldman,2020-10-16 20:19,Eclipse,66 +319,Myrle Blitz,2020-10-15 18:54,Truffle Sazerac,64 +320,Taite Lorenzetti,2020-10-15 21:18,Daiquiri,54 +321,Yam Mesicka,2020-10-15 23:48,Eclipse,66 +322,Yam Mesicka,2020-10-15 21:49,Eclipse,66 +323,Archibald Buggy,2020-10-15 23:03,Irish Coffee,54 +324,Priscella Gargett,2020-10-15 20:43,Eclipse,66 +325,Corrie Moogan,2020-10-14 23:24,Truffle Sazerac,64 +326,Urbain Freegard,2020-10-15 21:39,Daiquiri,54 +327,Megen Scragg,2020-10-16 18:35,Eclipse,66 +328,Nichol Benfell,2020-10-15 21:53,Eclipse,66 +329,Caye D'Enrico,2020-10-15 19:10,Truffle Sazerac,64 +330,Kiah Mogey,2020-10-15 20:31,Black Magic Julep,69 +331,Yam Mesicka,2020-10-15 19:46,Eclipse,66 +332,Gilberta Gainfort,2020-10-15 21:42,Eclipse,66 +333,Nyssa Rolfo,2020-10-15 21:06,Chartreuse Cobbler,61 +334,Dorothy Braisted,2020-10-14 23:30,Daiquiri,54 +335,Jaimie Epine,2020-10-16 20:59,Daiquiri,54 +336,Petra Fontaine,2020-10-15 20:26,Eclipse,66 +337,Sibella Kenright,2020-10-15 18:06,Daiquiri,54 +338,Anatollo Drake,2020-10-15 20:24,Eclipse,66 +339,Aurel Stoppard,2020-10-15 20:41,Black Magic Julep,69 +340,Sondra Eilhertsen,2020-10-15 18:33,Irish Coffee,54 +341,Ronit Feldman,2020-10-15 23:05,Irish Coffee,54 +342,Wittie Snap,2020-10-15 21:39,Eclipse,66 +343,Jephthah Gaize,2020-10-13 20:08,Eclipse,66 +344,Rianon Orme,2020-10-15 20:12,Truffle Sazerac,64 +345,Lanie Severy,2020-10-15 18:17,Eclipse,66 +346,Moshe Hamblyn,2020-10-15 18:18,Black Magic Julep,69 +347,Carline Newbery,2020-10-15 20:23,Eclipse,66 +348,Archibaldo Ferenc,2020-10-15 22:07,Chartreuse Cobbler,61 +349,Clovis Chidgey,2020-10-16 21:07,Eclipse,66 +350,Dillie Howie,2020-10-15 21:03,Eclipse,66 +351,Nat Winear,2020-10-15 21:13,Eclipse,66 +352,Yam Mesicka,2020-10-15 19:38,Eclipse,66 +353,Chrissy Paulat,2020-10-15 22:19,Eclipse,66 +354,Bettina Shermar,2020-10-15 21:24,Bloody Mary,57 +355,Yam Mesicka,2020-10-15 21:50,Chartreuse Cobbler,61 +356,Henryetta Antoniazzi,2020-10-14 23:58,Chartreuse Cobbler,61 +357,Yam Mesicka,2020-10-14 23:12,Bloody Mary,57 +358,Obidiah Tippell,2020-10-15 20:09,Eclipse,66 +359,Walsh McMoyer,2020-10-15 21:57,Eclipse,66 +360,Tera Halmkin,2020-10-15 20:44,Eclipse,66 +361,Donielle Stentiford,2020-10-17 21:59,Black Magic Julep,69 +362,Gwenora Nana,2020-10-15 20:09,Eclipse,66 +363,Dennie Grishukhin,2020-10-15 20:40,Truffle Sazerac,64 +364,Troy Brand,2020-10-16 18:35,Eclipse,66 +365,Vivyan Checcucci,2020-10-15 20:32,Eclipse,66 +366,Stacee Topper,2020-10-15 21:20,Chartreuse Cobbler,61 +367,Aileen Caswell,2020-10-15 18:51,Eclipse,66 +368,Jacinta Shedd,2020-10-16 18:46,Eclipse,66 +369,Florinda Pietrowicz,2020-10-15 22:07,Eclipse,66 +370,Clayson Glazier,2020-10-15 23:01,Truffle Sazerac,64 +371,Yam Mesicka,2020-10-15 19:33,Black Magic Julep,69 +372,Adria Butler,2020-10-15 19:50,Eclipse,66 +373,Elbert McCrohon,2020-10-15 21:06,Irish Coffee,54 +374,Dill Woolward,2020-10-15 21:12,Irish Coffee,54 +375,Eve Coger,2020-10-15 23:00,Irish Coffee,54 +376,Katya Bilsland,2020-10-15 20:17,Chartreuse Cobbler,61 +377,Tanner Gollard,2020-10-15 20:38,Eclipse,66 +378,Arv Slyford,2020-10-15 23:05,Daiquiri,54 +379,Rozalie OIlier,2020-10-16 22:05,Eclipse,66 +380,Viviana Oats,2020-10-15 18:46,Daiquiri,54 +381,Vinita Kezar,2020-10-15 20:35,Eclipse,66 +382,Jermayne Scolli,2020-10-15 21:30,Bloody Mary,57 +383,Yam Mesicka,2020-10-15 20:07,Bloody Mary,57 +384,Jody Feehily,2020-10-14 23:41,Eclipse,66 +385,Davita Oehme,2020-10-15 19:43,Daiquiri,54 +386,Christie Dorrington,2020-10-15 23:19,Eclipse,66 +387,Basia Sabati,2020-10-15 23:38,Eclipse,66 +388,Yam Mesicka,2020-10-15 21:53,Bloody Mary,57 +389,Verna Rawe,2020-10-16 19:24,Eclipse,66 +390,Orly Buckell,2020-10-16 18:04,Eclipse,66 +391,Nikita Southwood,2020-10-16 21:24,Daiquiri,54 +392,Edy Congdon,2020-10-15 19:08,Eclipse,66 +393,Gilda Hanscomb,2020-10-15 18:31,Truffle Sazerac,64 +394,Vito Hatry,2020-10-15 20:55,Black Magic Julep,69 +395,Baryram Farfolomeev,2020-10-15 19:25,Chartreuse Cobbler,61 +396,Noe Goldster,2020-10-16 18:08,Eclipse,66 +397,Eunice LAbbet,2020-10-15 21:54,Eclipse,66 +398,Leese Gilliatt,2020-10-15 22:03,Eclipse,66 +399,Jocelyne Songest,2020-10-15 21:14,Chartreuse Cobbler,61 +400,Jessamine Friar,2020-10-15 20:42,Eclipse,66 +401,Ronit Feldman,2020-10-15 19:55,Eclipse,66 +402,Ronit Feldman,2020-10-16 18:39,Eclipse,66 +403,Laurent Brookhouse,2020-10-15 20:45,Bloody Mary,57 +404,Thorny Woodbridge,2020-10-16 18:49,Eclipse,66 +405,Travus Robertazzi,2020-10-15 22:59,Eclipse,66 +406,Anette Yegorchenkov,2020-10-15 23:20,Eclipse,66 +407,Cassie Cosser,2020-10-15 20:56,Eclipse,66 +408,Aharon Knoton,2020-10-14 21:59,Chartreuse Cobbler,61 +409,Yam Mesicka,2020-10-15 20:36,Eclipse,66 +410,Saba Parradine,2020-10-15 19:22,Eclipse,66 +411,Yam Mesicka,2020-10-15 20:49,Black Magic Julep,69 +412,Ax Kleinbaum,2020-10-15 20:49,Eclipse,66 +413,Nicole Haddow,2020-10-15 21:39,Eclipse,66 +414,Gunner Middleditch,2020-10-15 23:53,Black Magic Julep,69 +415,Welby Goney,2020-10-15 19:10,Eclipse,66 +416,Wallis Birnie,2020-10-15 23:48,Daiquiri,54 +417,Karee Bowkett,2020-10-14 22:45,Daiquiri,54 +418,Carl Jankin,2020-10-15 21:34,Truffle Sazerac,64 +419,Dedie Bitterton,2020-10-15 20:45,Eclipse,66 +420,Debera Brisbane,2020-10-15 20:12,Bloody Mary,57 +421,Doralynn Latus,2020-10-15 19:58,Eclipse,66 +422,Sandye Jordeson,2020-10-15 19:00,Eclipse,66 +423,Ronit Feldman,2020-10-15 23:01,Eclipse,66 +424,Garek Mordin,2020-10-15 20:25,Truffle Sazerac,64 +425,Odelinda Sunner,2020-10-15 21:17,Eclipse,66 +426,Burgess Mantripp,2020-10-15 21:06,Eclipse,66 +427,Letisha Guinery,2020-10-15 20:02,Black Magic Julep,69 +428,Frederigo De'Ath,2020-10-14 23:42,Eclipse,66 +429,Mychal Pettyfar,2020-10-15 21:16,Daiquiri,54 +430,Madlin Airy,2020-10-15 21:09,Bloody Mary,57 +431,Ronit Feldman,2020-10-15 20:52,Black Magic Julep,69 +432,Fraser Fairholme,2020-10-14 23:23,Eclipse,66 +433,Danyelle Eddie,2020-10-15 20:23,Truffle Sazerac,64 +434,Peta Poag,2020-10-15 21:17,Eclipse,66 +435,Monte Librey,2020-10-16 20:40,Eclipse,66 +436,Cobbie Mourant,2020-10-15 22:08,Eclipse,66 +437,Paul Stovold,2020-10-15 21:26,Truffle Sazerac,64 +438,Kellen Crossby,2020-10-15 20:22,Eclipse,66 +439,Clevey Gullyes,2020-10-15 20:04,Eclipse,66 +440,Giustino Dingivan,2020-10-15 21:03,Eclipse,66 +441,Nevsa Lideard,2020-10-15 22:35,Eclipse,66 +442,Zsazsa Basnett,2020-10-15 19:52,Eclipse,66 +443,Adolphus Weight,2020-10-15 21:34,Chartreuse Cobbler,61 +444,Clovis Coslitt,2020-10-15 20:54,Eclipse,66 +445,Ronit Feldman,2020-10-15 23:58,Eclipse,66 +446,Kelley Templman,2020-10-15 21:01,Truffle Sazerac,64 +447,Paxon Womersley,2020-10-15 20:50,Daiquiri,54 +448,Ronit Feldman,2020-10-15 21:32,Bloody Mary,57 +449,Giana Abrahams,2020-10-15 23:24,Eclipse,66 +450,Hilarius Bedford,2020-10-15 20:51,Eclipse,66 +451,Yam Mesicka,2020-10-15 18:20,Eclipse,66 +452,Hilliard Dowker,2020-10-15 22:02,Irish Coffee,54 +453,Ewen Morling,2020-10-14 22:17,Eclipse,66 +454,Lombard Tincknell,2020-10-15 19:14,Eclipse,66 +455,Dee Halpen,2020-10-15 21:24,Chartreuse Cobbler,61 +456,Worthy Thorius,2020-10-15 23:13,Chartreuse Cobbler,61 +457,Elmira Larret,2020-10-15 20:58,Eclipse,66 +458,Malinda Choffin,2020-10-15 22:30,Chartreuse Cobbler,61 +459,Garret Whitmore,2020-10-14 22:54,Eclipse,66 +460,Maiga Surridge,2020-10-15 21:32,Truffle Sazerac,64 +461,Roslyn Campbell,2020-10-15 19:57,Chartreuse Cobbler,61 +462,Luther Pidcock,2020-10-15 21:14,Irish Coffee,54 +463,Isis Messruther,2020-10-15 21:32,Black Magic Julep,69 +464,Yam Mesicka,2020-10-15 18:29,Eclipse,66 +465,Ronit Feldman,2020-10-14 21:30,Eclipse,66 +466,Claretta Smalridge,2020-10-15 19:42,Eclipse,66 +467,Alethea Bygreaves,2020-10-15 20:22,Bloody Mary,57 +468,Bennett Delahunty,2020-10-15 23:07,Chartreuse Cobbler,61 +469,Nadia Minigo,2020-10-15 20:40,Eclipse,66 +470,Raeann Issakov,2020-10-15 20:24,Bloody Mary,57 +471,Marijo Johananoff,2020-10-15 20:05,Eclipse,66 +472,Gwenette Giffard,2020-10-15 22:11,Eclipse,66 +473,Giff Hollows,2020-10-15 20:31,Eclipse,66 +474,Yam Mesicka,2020-10-14 21:15,Irish Coffee,54 +475,Ronit Feldman,2020-10-15 21:47,Chartreuse Cobbler,61 +476,Petronia Carthy,2020-10-15 21:37,Eclipse,66 +477,Ronit Feldman,2020-10-15 20:16,Eclipse,66 +478,Godart Hilldrup,2020-10-15 23:24,Black Magic Julep,69 +479,Zorana Heinschke,2020-10-15 22:26,Eclipse,66 +480,Had Wakelin,2020-10-16 22:25,Eclipse,66 +481,Cesare Pingstone,2020-10-15 23:42,Truffle Sazerac,64 +482,Yard D'Onisi,2020-10-15 18:19,Bloody Mary,57 +483,Leontyne Hartwell,2020-10-15 18:02,Eclipse,66 +484,Trstram Gibbeson,2020-10-15 22:37,Eclipse,66 +485,Bondon Tight,2020-10-15 19:50,Eclipse,66 +486,Tuck Hundey,2020-10-15 21:06,Eclipse,66 +487,Faustine De Clairmont,2020-10-15 21:13,Bloody Mary,57 +488,Jessee Goffe,2020-10-15 20:58,Eclipse,66 +489,Carin Quickfall,2020-10-15 20:34,Black Magic Julep,69 +490,Mathe Tetford,2020-10-15 21:27,Eclipse,66 +491,Patin Dulson,2020-10-15 21:29,Truffle Sazerac,64 +492,Gretna Rasor,2020-10-15 19:54,Daiquiri,54 +493,Gare Foulsham,2020-10-15 20:52,Eclipse,66 +494,Aksel Quemby,2020-10-15 23:52,Black Magic Julep,69 +495,Janaya Fanning,2020-10-15 21:01,Eclipse,66 +496,Karry Lamblin,2020-10-15 21:55,Eclipse,66 +497,Silas Baddoe,2020-10-15 20:07,Black Magic Julep,69 +498,Peirce Leitche,2020-10-16 21:37,Black Magic Julep,69 +499,Dona Manie,2020-10-15 21:01,Eclipse,66 +500,Noby Emnoney,2020-10-15 20:55,Truffle Sazerac,64 +501,Nani Reignolds,2020-10-14 20:58,Eclipse,66 +502,Ruthi Sotham,2020-10-15 20:51,Eclipse,66 +503,Morry Fallowes,2020-10-15 21:41,Eclipse,66 +504,Bill Ritchie,2020-10-15 21:55,Eclipse,66 +505,Ferdinande Foden,2020-10-15 20:56,Black Magic Julep,69 +506,Lennie Squeers,2020-10-16 22:06,Bloody Mary,57 +507,Dominga McKinnon,2020-10-15 21:16,Irish Coffee,54 +508,Terese Blaksley,2020-10-15 21:55,Truffle Sazerac,64 +509,Jayme Dunham,2020-10-15 21:16,Black Magic Julep,69 +510,Lucias McCague,2020-10-15 20:59,Eclipse,66 +511,Yam Mesicka,2020-10-15 18:27,Bloody Mary,57 +512,Alley Hoodspeth,2020-10-15 21:47,Chartreuse Cobbler,61 +513,Haskel Gauchier,2020-10-15 18:26,Irish Coffee,54 +514,Desi Cutcliffe,2020-10-15 20:42,Truffle Sazerac,64 +515,Andonis Bourdas,2020-10-15 19:23,Eclipse,66 +516,Ronit Feldman,2020-10-15 22:25,Chartreuse Cobbler,61 +517,Carin Stoyles,2020-10-15 18:01,Black Magic Julep,69 +518,Hilario Clouston,2020-10-15 21:47,Black Magic Julep,69 +519,Ronit Feldman,2020-10-15 21:34,Eclipse,66 +520,Yam Mesicka,2020-10-15 20:59,Eclipse,66 +521,Torey Hanretty,2020-10-15 18:34,Eclipse,66 +522,Elaine Vanyatin,2020-10-16 22:46,Eclipse,66 +523,Abran Crickmoor,2020-10-15 21:13,Black Magic Julep,69 +524,Agata Simo,2020-10-15 22:14,Bloody Mary,57 +525,Ronit Feldman,2020-10-15 23:43,Chartreuse Cobbler,61 +526,Fred Kennerknecht,2020-10-14 22:49,Chartreuse Cobbler,61 +527,Dana Maxstead,2020-10-15 22:53,Chartreuse Cobbler,61 +528,Morgen Dimond,2020-10-15 20:02,Black Magic Julep,69 +529,Heriberto Mitcheson,2020-10-15 18:26,Irish Coffee,54 +530,Laurel Bramer,2020-10-14 20:26,Eclipse,66 +531,Lauri Crone,2020-10-15 20:28,Eclipse,66 +532,Merrie Carek,2020-10-16 18:17,Truffle Sazerac,64 +533,Borden Moult,2020-10-15 23:50,Truffle Sazerac,64 +534,Una Pregal,2020-10-15 23:03,Bloody Mary,57 +535,Selestina Northcliffe,2020-10-15 20:47,Chartreuse Cobbler,61 +536,Gayle Folbige,2020-10-15 21:39,Eclipse,66 +537,Matty Fenna,2020-10-15 20:14,Truffle Sazerac,64 +538,Ronit Feldman,2020-10-15 18:55,Eclipse,66 +539,Genvieve Simanek,2020-10-15 23:09,Bloody Mary,57 +540,Ronit Feldman,2020-10-15 20:01,Eclipse,66 +541,Hadria Mardell,2020-10-15 22:48,Daiquiri,54 +542,Tucky Mathiassen,2020-10-15 20:36,Irish Coffee,54 +543,Kissie Orrocks,2020-10-15 22:42,Chartreuse Cobbler,61 +544,Annabella Coughan,2020-10-15 20:48,Bloody Mary,57 +545,Yam Mesicka,2020-10-15 20:22,Eclipse,66 +546,Kimbell Larwood,2020-10-16 18:31,Eclipse,66 +547,Scarlett Sivil,2020-10-15 21:46,Chartreuse Cobbler,61 +548,Hillie Elvins,2020-10-14 22:06,Eclipse,66 +549,Cecelia Proschek,2020-10-15 21:24,Eclipse,66 +550,Micki Jarmaine,2020-10-14 20:51,Black Magic Julep,69 +551,Nollie Killingbeck,2020-10-15 22:32,Eclipse,66 +552,Estrellita Wealthall,2020-10-17 18:39,Eclipse,66 +553,Jude Lace,2020-10-15 21:07,Daiquiri,54 +554,Conni Gomersall,2020-10-14 21:11,Chartreuse Cobbler,61 +555,Ronit Feldman,2020-10-15 20:41,Eclipse,66 +556,Mavra Moehler,2020-10-15 21:06,Eclipse,66 +557,Yam Mesicka,2020-10-15 22:23,Eclipse,66 +558,Ronit Feldman,2020-10-15 21:46,Eclipse,66 +559,Shamus Dowthwaite,2020-10-15 20:54,Eclipse,66 +560,Izzy Feldman,2020-10-14 23:12,Eclipse,66 +561,Kaycee Drewes,2020-10-15 22:28,Eclipse,66 +562,Riannon Attwill,2020-10-15 22:15,Bloody Mary,57 +563,Tades Baurerich,2020-10-15 20:50,Eclipse,66 +564,Mora Padkin,2020-10-15 23:15,Eclipse,66 +565,Tori Tappor,2020-10-15 20:16,Eclipse,66 +566,Moira Camilio,2020-10-15 20:10,Bloody Mary,57 +567,Stillmann Smurfitt,2020-10-16 19:35,Eclipse,66 +568,Fax Damant,2020-10-15 21:36,Bloody Mary,57 +569,Kynthia Machin,2020-10-15 21:13,Irish Coffee,54 +570,Ninnetta Karpmann,2020-10-15 18:57,Black Magic Julep,69 +571,Elijah Blown,2020-10-15 20:48,Eclipse,66 +572,Mike Alessandretti,2020-10-15 19:46,Chartreuse Cobbler,61 +573,Ronit Feldman,2020-10-15 23:39,Irish Coffee,54 +574,Bobinette Lockton,2020-10-15 22:05,Chartreuse Cobbler,61 +575,Tonye Wilde,2020-10-15 21:56,Eclipse,66 +576,Che Pridmore,2020-10-15 23:40,Eclipse,66 +577,Jillane Detoc,2020-10-15 22:48,Black Magic Julep,69 +578,Rinaldo Lapenna,2020-10-15 20:02,Black Magic Julep,69 +579,Davey Botha,2020-10-15 20:46,Chartreuse Cobbler,61 +580,Britt Lavies,2020-10-15 18:51,Black Magic Julep,69 +581,Vitia Riatt,2020-10-15 21:58,Daiquiri,54 +582,Benita Fleetham,2020-10-15 22:17,Eclipse,66 +583,Steffen Jaycock,2020-10-15 23:22,Irish Coffee,54 +584,Mathian Buckbee,2020-10-15 20:25,Eclipse,66 +585,Alic Rowly,2020-10-14 19:08,Chartreuse Cobbler,61 +586,Koo Chudleigh,2020-10-15 21:21,Bloody Mary,57 +587,Ronit Feldman,2020-10-16 23:49,Eclipse,66 +588,Dominga Dewitt,2020-10-14 23:57,Truffle Sazerac,64 +589,Kriste Kocher,2020-10-15 20:12,Eclipse,66 +590,Heywood Smeeton,2020-10-14 23:04,Black Magic Julep,69 +591,Sallee Hatcher,2020-10-15 21:00,Eclipse,66 +592,Alfreda Milburn,2020-10-15 23:52,Eclipse,66 +593,Nancy Whitewood,2020-10-15 21:59,Eclipse,66 +594,Flossi Walley,2020-10-16 19:57,Truffle Sazerac,64 +595,Fernando Vallerine,2020-10-15 21:22,Eclipse,66 +596,Bailie Siviour,2020-10-15 23:31,Eclipse,66 +597,Stacee Provest,2020-10-15 20:33,Black Magic Julep,69 +598,Ernesta Ragless,2020-10-15 20:40,Bloody Mary,57 +599,Ronit Feldman,2020-10-15 21:16,Eclipse,66 +600,Ilyssa Cradock,2020-10-14 23:39,Eclipse,66 +601,Maressa Gerlack,2020-10-15 21:13,Black Magic Julep,69 +602,Mauricio Havock,2020-10-16 22:17,Daiquiri,54 +603,Pippa Maryon,2020-10-15 21:19,Eclipse,66 +604,Allyn Riddel,2020-10-15 23:42,Eclipse,66 +605,Matias Rapa,2020-10-15 18:50,Eclipse,66 +606,Ash Casserly,2020-10-15 22:52,Black Magic Julep,69 +607,Rouvin Inchcomb,2020-10-15 21:31,Irish Coffee,54 +608,Fifi Brownsall,2020-10-15 20:43,Eclipse,66 +609,Burlie Pillman,2020-10-15 22:20,Eclipse,66 +610,Cathyleen Larrett,2020-10-15 20:42,Eclipse,66 +611,Ronit Feldman,2020-10-15 23:33,Chartreuse Cobbler,61 +612,Skylar Anchor,2020-10-15 20:02,Eclipse,66 +613,Adelaide Terne,2020-10-16 18:27,Eclipse,66 +614,Fanchette Feehan,2020-10-15 20:41,Eclipse,66 +615,Pooh Bouda,2020-10-15 20:34,Eclipse,66 +616,Bekki Grenfell,2020-10-16 20:35,Eclipse,66 +617,Walther Haack,2020-10-14 22:38,Truffle Sazerac,64 +618,Estelle Saban,2020-10-16 20:20,Daiquiri,54 +619,Ferguson McLenaghan,2020-10-15 19:44,Eclipse,66 +620,Trent Caudrey,2020-10-15 21:22,Black Magic Julep,69 +621,Shanta Fenech,2020-10-15 21:06,Eclipse,66 +622,Ian Godain,2020-10-15 20:58,Truffle Sazerac,64 +623,Timmie Zumbusch,2020-10-15 20:18,Eclipse,66 +624,Ram Cullrford,2020-10-15 20:51,Chartreuse Cobbler,61 +625,Penn Boraston,2020-10-15 19:36,Truffle Sazerac,64 +626,Sebastiano Newling,2020-10-15 19:35,Eclipse,66 +627,Stevena Slograve,2020-10-15 21:52,Eclipse,66 +628,Bary Oxbie,2020-10-16 18:52,Bloody Mary,57 +629,Waldo Parzis,2020-10-15 23:48,Eclipse,66 +630,Tobey Wilds,2020-10-15 21:07,Eclipse,66 +631,Eada Madner,2020-10-15 22:41,Eclipse,66 +632,Alexandrina Ackers,2020-10-15 22:19,Black Magic Julep,69 +633,Breena Reavell,2020-10-15 21:14,Eclipse,66 +634,Madalyn Kinzel,2020-10-15 19:45,Irish Coffee,54 +635,Delphinia Anneslie,2020-10-15 18:59,Eclipse,66 +636,Ingmar Ghilardi,2020-10-15 19:07,Eclipse,66 +637,Alasteir Falkinder,2020-10-15 22:40,Black Magic Julep,69 +638,Elaina Larkins,2020-10-14 22:55,Eclipse,66 +639,Andromache Seger,2020-10-16 21:37,Eclipse,66 +640,Jule Killeley,2020-10-15 19:48,Irish Coffee,54 +641,Kaspar Killgus,2020-10-15 18:42,Eclipse,66 +642,Reta Dowse,2020-10-15 23:01,Daiquiri,54 +643,Mervin Addekin,2020-10-15 19:13,Eclipse,66 +644,Karlee Rubin,2020-10-15 21:59,Daiquiri,54 +645,Ronit Feldman,2020-10-15 21:49,Eclipse,66 +646,Auroora Figg,2020-10-15 19:33,Eclipse,66 +647,Ebeneser McLaine,2020-10-15 18:46,Bloody Mary,57 +648,Quintana Allnutt,2020-10-15 22:49,Eclipse,66 +649,Rolando Skill,2020-10-14 23:39,Eclipse,66 +650,Kipper Archer,2020-10-15 21:28,Bloody Mary,57 +651,Teador Ginie,2020-10-15 20:55,Daiquiri,54 +652,Kimmi Gainseford,2020-10-15 20:51,Chartreuse Cobbler,61 +653,Eachelle Booth-Jarvis,2020-10-15 19:59,Truffle Sazerac,64 +654,Ruthi Elcoate,2020-10-15 19:10,Eclipse,66 +655,Shandie Whitrod,2020-10-15 21:40,Eclipse,66 +656,Linnell Gouldbourn,2020-10-14 22:55,Eclipse,66 +657,Ronit Feldman,2020-10-15 21:31,Eclipse,66 +658,Yam Mesicka,2020-10-15 19:48,Eclipse,66 +659,Tod Norbury,2020-10-15 20:09,Irish Coffee,54 +660,Caria Argile,2020-10-15 18:11,Eclipse,66 +661,Shawnee Janicki,2020-10-15 23:40,Eclipse,66 +662,Zachery Pulsford,2020-10-15 22:16,Eclipse,66 +663,Ronit Feldman,2020-10-14 23:12,Eclipse,66 +664,Elinor Matcham,2020-10-15 18:56,Eclipse,66 +665,Lloyd Boake,2020-10-14 23:01,Black Magic Julep,69 +666,Bradly Steven,2020-10-15 19:58,Daiquiri,54 +667,Gwendolin Feedham,2020-10-14 23:39,Eclipse,66 +668,Lonni Duligal,2020-10-15 22:16,Bloody Mary,57 +669,Cord Chestle,2020-10-15 18:15,Bloody Mary,57 +670,Fran Simo,2020-10-15 23:18,Chartreuse Cobbler,61 +671,Alyce Weigh,2020-10-15 21:02,Black Magic Julep,69 +672,Olia Louch,2020-10-15 21:35,Bloody Mary,57 +673,Correy Sibbit,2020-10-15 21:42,Eclipse,66 +674,Raf Penhaleurack,2020-10-15 18:43,Eclipse,66 +675,Dimitri Dennison,2020-10-15 18:29,Truffle Sazerac,64 +676,Ronit Feldman,2020-10-15 20:16,Daiquiri,54 +677,Jamima Wegman,2020-10-15 20:13,Eclipse,66 +678,Ronit Feldman,2020-10-14 23:13,Eclipse,66 +679,Cherilynn Howett,2020-10-15 21:55,Eclipse,66 +680,Rafferty Gillise,2020-10-15 20:16,Eclipse,66 +681,Ronit Feldman,2020-10-15 20:40,Bloody Mary,57 +682,Lanita Reburn,2020-10-15 18:19,Eclipse,66 +683,Tamar Eschalette,2020-10-15 18:42,Eclipse,66 +684,Ronit Feldman,2020-10-15 21:53,Bloody Mary,57 +685,Yam Mesicka,2020-10-15 21:18,Eclipse,66 +686,Yam Mesicka,2020-10-15 18:19,Eclipse,66 +687,Cacilia Mandell,2020-10-16 18:38,Daiquiri,54 +688,Heindrick McNysche,2020-10-16 18:08,Irish Coffee,54 +689,Felike Pennrington,2020-10-15 21:23,Daiquiri,54 +690,Tasia Leask,2020-10-15 19:31,Truffle Sazerac,64 +691,Cecilio Erlam,2020-10-14 23:50,Black Magic Julep,69 +692,Delmore Chedzoy,2020-10-15 21:31,Bloody Mary,57 +693,Tonye Doubleday,2020-10-15 21:31,Daiquiri,54 +694,Alfie Wackett,2020-10-15 21:15,Eclipse,66 +695,Petr Flatte,2020-10-14 23:45,Eclipse,66 +696,Ginnie Whetson,2020-10-14 22:30,Truffle Sazerac,64 +697,Dallas Southby,2020-10-15 23:39,Eclipse,66 +698,Del Upjohn,2020-10-16 21:59,Eclipse,66 +699,Ronit Feldman,2020-10-15 20:58,Eclipse,66 +700,Evanne Cotterel,2020-10-15 21:26,Black Magic Julep,69 +701,Babs Panichelli,2020-10-15 21:31,Eclipse,66 +702,Baillie Dyment,2020-10-15 20:17,Eclipse,66 +703,Rudd Gunderson,2020-10-15 20:06,Black Magic Julep,69 +704,Ronit Feldman,2020-10-15 20:11,Eclipse,66 +705,Kit Lovitt,2020-10-15 21:17,Bloody Mary,57 +706,Marty Fry,2020-10-15 23:00,Eclipse,66 +707,Emlyn Galland,2020-10-15 20:58,Daiquiri,54 +708,Natty Caccavella,2020-10-15 22:18,Truffle Sazerac,64 +709,Yam Mesicka,2020-10-15 21:17,Eclipse,66 +710,John Stevings,2020-10-15 23:50,Eclipse,66 +711,Marysa Marcam,2020-10-15 19:43,Irish Coffee,54 +712,Alvis Mincini,2020-10-15 22:43,Truffle Sazerac,64 +713,Torre Chittey,2020-10-15 20:28,Eclipse,66 +714,Annamaria Woodrooffe,2020-10-15 21:16,Eclipse,66 +715,Stacee Forty,2020-10-15 20:59,Truffle Sazerac,64 +716,Vivie Minister,2020-10-15 18:51,Eclipse,66 +717,Brooks Pumfrey,2020-10-14 23:09,Irish Coffee,54 +718,Elsworth Fink,2020-10-15 21:07,Chartreuse Cobbler,61 +719,Arabele MacGiolla Pheadair,2020-10-15 19:44,Eclipse,66 +720,Pattie Sealove,2020-10-15 20:49,Eclipse,66 +721,Jolie Woolmore,2020-10-16 20:32,Eclipse,66 +722,Hildagarde Conochie,2020-10-15 20:02,Bloody Mary,57 +723,Kenneth Pettie,2020-10-15 21:17,Eclipse,66 +724,Leta Wilshire,2020-10-15 21:34,Eclipse,66 +725,Yam Mesicka,2020-10-15 20:14,Eclipse,66 +726,Lew Cansfield,2020-10-15 19:13,Eclipse,66 +727,Ibbie Froud,2020-10-15 18:12,Eclipse,66 +728,Sydney Frier,2020-10-15 21:03,Daiquiri,54 +729,Murry Pleass,2020-10-15 22:31,Eclipse,66 +730,Walton Atcherley,2020-10-14 23:36,Eclipse,66 +731,Petronella Ruoff,2020-10-16 18:11,Bloody Mary,57 +732,Nissa Binns,2020-10-15 20:33,Eclipse,66 +733,Dynah Prescott,2020-10-15 20:44,Eclipse,66 +734,Drusi Doward,2020-10-15 20:46,Eclipse,66 +735,Gaynor Manilo,2020-10-15 23:04,Eclipse,66 +736,Garrot Gavrielli,2020-10-15 19:38,Eclipse,66 +737,Brear Pyer,2020-10-15 21:10,Chartreuse Cobbler,61 +738,Veda Pawle,2020-10-15 21:06,Eclipse,66 +739,Tye Cupitt,2020-10-15 21:55,Daiquiri,54 +740,Jo ann Lawty,2020-10-15 21:41,Eclipse,66 +741,Jayme Tripe,2020-10-15 22:58,Truffle Sazerac,64 +742,Luella Melledy,2020-10-15 21:54,Eclipse,66 +743,Kipp Lydiard,2020-10-15 22:16,Chartreuse Cobbler,61 +744,Romain Patrick,2020-10-14 23:47,Eclipse,66 +745,Yam Mesicka,2020-10-15 20:05,Black Magic Julep,69 +746,Ninnetta Blaszczynski,2020-10-15 18:36,Bloody Mary,57 +747,Maurise Lawther,2020-10-14 21:32,Truffle Sazerac,64 +748,Brendan Butterley,2020-10-15 22:07,Eclipse,66 +749,Lek Saw,2020-10-15 23:32,Eclipse,66 +750,Ruperta Alentyev,2020-10-14 21:22,Eclipse,66 +751,Cosimo Lyptrade,2020-10-16 20:05,Bloody Mary,57 +752,Dolly Spaunton,2020-10-15 23:55,Eclipse,66 +753,Jacques Hoofe,2020-10-15 23:08,Eclipse,66 +754,Kristien Doggett,2020-10-16 18:05,Black Magic Julep,69 +755,Welsh Olohan,2020-10-15 21:05,Eclipse,66 +756,Elane Baitson,2020-10-15 20:26,Eclipse,66 +757,Trevar Burton,2020-10-15 21:42,Chartreuse Cobbler,61 +758,Andre Fillingham,2020-10-16 18:59,Irish Coffee,54 +759,Romola Verick,2020-10-15 20:44,Black Magic Julep,69 +760,Vonnie Fearey,2020-10-15 22:39,Eclipse,66 +761,Garrett Buffery,2020-10-15 19:25,Black Magic Julep,69 +762,Caroline Dougill,2020-10-15 21:51,Eclipse,66 +763,Guenna Trematick,2020-10-15 21:45,Truffle Sazerac,64 +764,Kara-lynn Oblein,2020-10-15 23:41,Daiquiri,54 +765,Fleming Rabat,2020-10-15 22:59,Eclipse,66 +766,Yam Mesicka,2020-10-14 23:47,Eclipse,66 +767,Hugh Downage,2020-10-15 21:45,Chartreuse Cobbler,61 +768,Janenna Sauniere,2020-10-16 18:21,Eclipse,66 +769,Xenia Gravatt,2020-10-15 19:02,Eclipse,66 +770,Cosimo Castelow,2020-10-15 22:57,Eclipse,66 +771,Tobin Crake,2020-10-15 20:09,Eclipse,66 +772,Bogart Penrose,2020-10-15 21:55,Black Magic Julep,69 +773,Jodi Knowlden,2020-10-15 21:40,Eclipse,66 +774,Yam Mesicka,2020-10-15 18:42,Chartreuse Cobbler,61 +775,Ruttger Hillitt,2020-10-15 22:03,Daiquiri,54 +776,Harmony Gater,2020-10-15 20:02,Truffle Sazerac,64 +777,Roseanna Bernhard,2020-10-15 20:28,Eclipse,66 +778,Glyn Drepp,2020-10-15 20:25,Eclipse,66 +779,Anita Kay,2020-10-15 20:25,Eclipse,66 +780,Yam Mesicka,2020-10-15 20:59,Black Magic Julep,69 +781,Tracy Napier,2020-10-15 21:11,Black Magic Julep,69 +782,Sherrie Smitherham,2020-10-15 21:32,Bloody Mary,57 +783,Casper D' Angelo,2020-10-15 20:52,Eclipse,66 +784,Tan Baudin,2020-10-15 19:43,Eclipse,66 +785,Tan Lindell,2020-10-15 23:18,Eclipse,66 +786,Eliza Leate,2020-10-15 21:04,Eclipse,66 +787,Odelia Thome,2020-10-15 19:34,Bloody Mary,57 +788,Rhodia Duley,2020-10-15 19:58,Chartreuse Cobbler,61 +789,Ronit Feldman,2020-10-14 22:36,Truffle Sazerac,64 +790,Cissy Cosham,2020-10-15 20:35,Daiquiri,54 +791,Krissy Donnel,2020-10-15 22:15,Eclipse,66 +792,Anallese Ibbitson,2020-10-15 21:48,Eclipse,66 +793,Berkie Faughey,2020-10-15 19:07,Truffle Sazerac,64 +794,Uriah Ritson,2020-10-15 18:54,Irish Coffee,54 +795,Marwin Boliver,2020-10-15 21:16,Bloody Mary,57 +796,Yam Mesicka,2020-10-15 20:50,Chartreuse Cobbler,61 +797,Amy Guilbert,2020-10-15 18:39,Eclipse,66 +798,Louie Eldrett,2020-10-15 20:55,Irish Coffee,54 +799,Marylinda MacVean,2020-10-15 20:48,Eclipse,66 +800,Anallese Kingaby,2020-10-15 20:34,Bloody Mary,57 +801,Dania Keitch,2020-10-16 19:12,Daiquiri,54 +802,Cordell Brettoner,2020-10-15 21:28,Chartreuse Cobbler,61 +803,Vina Devonport,2020-10-16 21:00,Eclipse,66 +804,Polly Haibel,2020-10-15 21:33,Irish Coffee,54 +805,Annalee Yurikov,2020-10-15 19:26,Eclipse,66 +806,Annmaria Genike,2020-10-14 22:36,Irish Coffee,54 +807,Rozanne O'Flannery,2020-10-15 21:12,Black Magic Julep,69 +808,Clevie Graine,2020-10-15 20:24,Eclipse,66 +809,Felizio Dinkin,2020-10-14 21:13,Eclipse,66 +810,Kylynn Ogus,2020-10-15 23:35,Eclipse,66 +811,Neron Gann,2020-10-14 22:02,Eclipse,66 +812,Dale Kunkler,2020-10-15 23:52,Eclipse,66 +813,Yam Mesicka,2020-10-15 23:24,Eclipse,66 +814,Rozalie McComiskey,2020-10-15 22:00,Irish Coffee,54 +815,Keir Clausen-Thue,2020-10-15 22:46,Eclipse,66 +816,Joya MacKomb,2020-10-16 19:02,Truffle Sazerac,64 +817,Lynsey Pinxton,2020-10-15 18:44,Daiquiri,54 +818,Tamqrah Haine,2020-10-15 23:21,Eclipse,66 +819,Catrina Allden,2020-10-15 20:34,Eclipse,66 +820,Portia Dyott,2020-10-15 20:32,Chartreuse Cobbler,61 +821,Yam Mesicka,2020-10-15 19:16,Eclipse,66 +822,Gib Josovitz,2020-10-14 23:26,Eclipse,66 +823,Datha Brikner,2020-10-15 20:03,Chartreuse Cobbler,61 +824,Joyous Seedull,2020-10-16 18:45,Eclipse,66 +825,Bettine Cousens,2020-10-15 22:06,Eclipse,66 +826,Rhodia Prettyjohn,2020-10-16 21:26,Daiquiri,54 +827,Paxton Girardi,2020-10-15 20:33,Irish Coffee,54 +828,Kailey Iglesia,2020-10-15 21:40,Eclipse,66 +829,Doralia Nettles,2020-10-15 19:06,Irish Coffee,54 +830,Jaquenetta Nickolls,2020-10-15 21:47,Truffle Sazerac,64 +831,Ardisj Twaits,2020-10-15 19:50,Eclipse,66 +832,Jilli Murkus,2020-10-15 21:09,Irish Coffee,54 +833,Son Luddy,2020-10-15 18:22,Truffle Sazerac,64 +834,Gilly Redhole,2020-10-15 19:10,Truffle Sazerac,64 +835,Corilla Wims,2020-10-15 22:33,Eclipse,66 +836,Trina Drew,2020-10-15 20:16,Chartreuse Cobbler,61 +837,Emiline Scoone,2020-10-15 20:46,Chartreuse Cobbler,61 +838,Lind Adhams,2020-10-15 20:43,Black Magic Julep,69 +839,Lucho Johansson,2020-10-15 21:01,Eclipse,66 +840,Jedd Whorlton,2020-10-15 18:46,Eclipse,66 +841,Christine Askell,2020-10-16 18:04,Irish Coffee,54 +842,Ronit Feldman,2020-10-15 20:31,Eclipse,66 +843,Trix Filde,2020-10-15 20:38,Truffle Sazerac,64 +844,Mattie Kinnie,2020-10-15 20:34,Eclipse,66 +845,Christopher Burfitt,2020-10-15 22:11,Irish Coffee,54 +846,Christa Tubbs,2020-10-15 23:17,Eclipse,66 +847,Deane Pellingar,2020-10-15 20:14,Eclipse,66 +848,Alysa Stoyle,2020-10-15 21:29,Eclipse,66 +849,Byron Handover,2020-10-14 21:55,Black Magic Julep,69 +850,Lotty Crow,2020-10-16 18:13,Chartreuse Cobbler,61 +851,Mozes Jacklings,2020-10-15 20:44,Eclipse,66 +852,Nat Loakes,2020-10-15 23:40,Chartreuse Cobbler,61 +853,Jacobo Crozier,2020-10-15 21:18,Black Magic Julep,69 +854,Renaud Bricknell,2020-10-14 22:34,Eclipse,66 +855,Yam Mesicka,2020-10-15 19:51,Eclipse,66 +856,Vince Kynsey,2020-10-15 23:26,Eclipse,66 +857,Gabbi Kid,2020-10-14 19:10,Irish Coffee,54 +858,Lee Dunmore,2020-10-15 19:38,Eclipse,66 +859,Ashlen Ledgard,2020-10-15 21:01,Daiquiri,54 +860,Pascale Oddey,2020-10-15 21:01,Bloody Mary,57 +861,Tiena Tinn,2020-10-15 18:58,Bloody Mary,57 +862,Hailey Hinz,2020-10-15 19:31,Eclipse,66 +863,Austine Tozer,2020-10-14 22:41,Bloody Mary,57 +864,Ronit Feldman,2020-10-15 21:06,Eclipse,66 +865,Frankie Nertney,2020-10-15 21:18,Eclipse,66 +866,Renado Gude,2020-10-14 18:09,Eclipse,66 +867,Joshua Rimmington,2020-10-14 23:32,Eclipse,66 +868,Lisette Benbow,2020-10-15 19:46,Black Magic Julep,69 +869,Andros Becom,2020-10-15 21:46,Eclipse,66 +870,Arlina Credland,2020-10-15 22:36,Bloody Mary,57 +871,Roda Cox,2020-10-15 22:15,Eclipse,66 +872,Tallulah Lepick,2020-10-14 20:50,Eclipse,66 +873,Ronit Feldman,2020-10-15 20:30,Eclipse,66 +874,Ryon Martinets,2020-10-15 21:10,Chartreuse Cobbler,61 +875,Giulia O'Spellissey,2020-10-15 20:53,Eclipse,66 +876,Lauraine Goldup,2020-10-15 19:50,Eclipse,66 +877,Ernestus Crolla,2020-10-14 22:04,Eclipse,66 +878,Pam Okenfold,2020-10-15 22:07,Eclipse,66 +879,Hubey Gathercole,2020-10-15 21:18,Eclipse,66 +880,Winona Wileman,2020-10-16 20:16,Eclipse,66 +881,Addison Faustin,2020-10-15 21:57,Eclipse,66 +882,Jeane Picot,2020-10-15 20:10,Irish Coffee,54 +883,Nicolea Firle,2020-10-15 19:16,Irish Coffee,54 +884,Marwin Benneton,2020-10-15 21:29,Black Magic Julep,69 +885,Gabbie Mehew,2020-10-16 19:42,Eclipse,66 +886,Durant Adamoli,2020-10-15 21:37,Eclipse,66 +887,Valerye Schole,2020-10-15 22:46,Eclipse,66 +888,Zack Gislebert,2020-10-15 21:11,Eclipse,66 +889,Chevalier Creggan,2020-10-14 21:34,Irish Coffee,54 +890,Silvio Keep,2020-10-14 21:57,Daiquiri,54 +891,Corrine Cota,2020-10-15 19:08,Irish Coffee,54 +892,Max Darker,2020-10-15 20:57,Bloody Mary,57 +893,Saidee Starsmore,2020-10-16 18:16,Daiquiri,54 +894,Marji Headey,2020-10-14 20:34,Eclipse,66 +895,Ronit Feldman,2020-10-16 19:53,Eclipse,66 +896,Aluino Kobiela,2020-10-15 22:24,Daiquiri,54 +897,Karisa Hanniger,2020-10-15 19:20,Eclipse,66 +898,Grayce Berkley,2020-10-14 21:04,Black Magic Julep,69 +899,Courtney Betjeman,2020-10-15 20:36,Truffle Sazerac,64 +900,Mickie Westby,2020-10-15 19:59,Bloody Mary,57 +901,Ansley Jan,2020-10-15 19:59,Bloody Mary,57 +902,Emmalyn Jencken,2020-10-15 20:58,Eclipse,66 +903,Broderic Haslam,2020-10-15 20:11,Eclipse,66 +904,Amelie Boughen,2020-10-15 20:30,Eclipse,66 +905,Yam Mesicka,2020-10-15 20:57,Eclipse,66 +906,Caril Dredge,2020-10-15 21:32,Black Magic Julep,69 +907,Isidora Wittrington,2020-10-15 23:59,Chartreuse Cobbler,61 +908,Sibbie Grzegorzewicz,2020-10-15 22:29,Daiquiri,54 +909,Yam Mesicka,2020-10-15 20:24,Eclipse,66 +910,Gaven Von Der Empten,2020-10-15 22:35,Eclipse,66 +911,Marcie Vickress,2020-10-14 22:04,Eclipse,66 +912,Avie McGilroy,2020-10-16 18:14,Eclipse,66 +913,Lebbie Loudyan,2020-10-15 21:12,Eclipse,66 +914,Filmer Pennetta,2020-10-16 18:38,Chartreuse Cobbler,61 +915,Rocky Dumbrall,2020-10-14 23:55,Chartreuse Cobbler,61 +916,Yam Mesicka,2020-10-15 21:08,Eclipse,66 +917,Pincus Studdeard,2020-10-16 18:53,Truffle Sazerac,64 +918,Sella Patient,2020-10-15 23:11,Eclipse,66 +919,Dolph Dispencer,2020-10-15 21:23,Bloody Mary,57 +920,Stanleigh Chaloner,2020-10-15 22:41,Eclipse,66 +921,Cherry Klima,2020-10-15 21:18,Eclipse,66 +922,Nonnah Barnfather,2020-10-15 21:22,Eclipse,66 +923,Red Wyldbore,2020-10-15 19:13,Chartreuse Cobbler,61 +924,Ronit Feldman,2020-10-15 22:27,Eclipse,66 +925,Rina Grinikhin,2020-10-15 20:28,Eclipse,66 +926,Danica Churchman,2020-10-15 20:02,Black Magic Julep,69 +927,Jerrold Bailiss,2020-10-16 19:26,Eclipse,66 +928,Welby Lodwick,2020-10-15 20:56,Truffle Sazerac,64 +929,Coretta Dellar,2020-10-15 20:00,Bloody Mary,57 +930,Antonietta Reolfi,2020-10-15 19:47,Eclipse,66 +931,Leandra Alessandone,2020-10-15 20:25,Daiquiri,54 +932,Remy Matschuk,2020-10-15 21:03,Eclipse,66 +933,Thorstein Craister,2020-10-15 20:09,Black Magic Julep,69 +934,Martynne Poundsford,2020-10-15 19:57,Black Magic Julep,69 +935,Stanwood Worviell,2020-10-15 21:10,Chartreuse Cobbler,61 +936,Gavan Tallant,2020-10-15 21:15,Daiquiri,54 +937,Ronit Feldman,2020-10-15 22:07,Truffle Sazerac,64 +938,Ronit Feldman,2020-10-15 19:44,Eclipse,66 +939,Sheff Losbie,2020-10-15 22:29,Chartreuse Cobbler,61 +940,Katee Baudi,2020-10-15 21:05,Eclipse,66 +941,Alisander Readwing,2020-10-15 22:38,Truffle Sazerac,64 +942,Miran Dufaur,2020-10-15 21:03,Eclipse,66 +943,Rolf Minchindon,2020-10-15 20:21,Eclipse,66 +944,Leese Ewan,2020-10-15 19:52,Eclipse,66 +945,Yam Mesicka,2020-10-15 23:49,Eclipse,66 +946,Martainn Rutley,2020-10-15 22:56,Eclipse,66 +947,Jed Gheerhaert,2020-10-15 21:10,Bloody Mary,57 +948,Bridgette Kemshell,2020-10-15 22:32,Truffle Sazerac,64 +949,Con Legion,2020-10-15 20:31,Eclipse,66 +950,Ronit Feldman,2020-10-15 19:38,Eclipse,66 +951,Kilian Mc Caghan,2020-10-14 22:51,Eclipse,66 +952,Bert Ibbott,2020-10-15 19:23,Irish Coffee,54 +953,Ronit Feldman,2020-10-15 21:12,Eclipse,66 +954,Syd Cuvley,2020-10-15 20:41,Eclipse,66 +955,Yam Mesicka,2020-10-15 20:39,Irish Coffee,54 +956,Marv Swindlehurst,2020-10-15 20:49,Eclipse,66 +957,Katherina Brodie,2020-10-15 23:24,Eclipse,66 +958,Janel Titchener,2020-10-15 20:33,Eclipse,66 +959,Yam Mesicka,2020-10-15 23:44,Truffle Sazerac,64 +960,Rafaelia Aleveque,2020-10-15 22:17,Chartreuse Cobbler,61 +961,Lettie Penas,2020-10-16 20:07,Eclipse,66 +962,Normy Trahearn,2020-10-15 19:31,Chartreuse Cobbler,61 +963,Mildrid Humbey,2020-10-15 22:17,Truffle Sazerac,64 +964,Yam Mesicka,2020-10-15 21:02,Eclipse,66 +965,Zondra Gircke,2020-10-15 21:54,Bloody Mary,57 +966,Giorgia Caff,2020-10-15 22:05,Irish Coffee,54 +967,Beaufort Tweede,2020-10-15 21:09,Bloody Mary,57 +968,Yam Mesicka,2020-10-15 20:29,Daiquiri,54 +969,Blanca Raulin,2020-10-15 21:24,Eclipse,66 +970,Rosalind Eagland,2020-10-15 21:36,Truffle Sazerac,64 +971,Zitella Twaite,2020-10-15 19:56,Eclipse,66 +972,Killian Dullingham,2020-10-15 18:32,Black Magic Julep,69 +973,Yam Mesicka,2020-10-15 23:35,Eclipse,66 +974,Yam Mesicka,2020-10-15 21:25,Eclipse,66 +975,Gideon Vasyanin,2020-10-16 20:58,Eclipse,66 +976,Lori Buddell,2020-10-15 21:06,Eclipse,66 +977,Jayson Normanvill,2020-10-15 21:08,Eclipse,66 +978,Dominique Maldin,2020-10-15 23:37,Black Magic Julep,69 +979,Orson Harbor,2020-10-15 23:11,Eclipse,66 +980,Courtnay Grishelyov,2020-10-15 20:11,Eclipse,66 +981,Matthias Greenhalf,2020-10-15 22:48,Eclipse,66 +982,Anthiathia Tydd,2020-10-15 18:15,Eclipse,66 +983,Angelle Cadamy,2020-10-15 21:30,Daiquiri,54 +984,Pacorro Husher,2020-10-15 20:52,Black Magic Julep,69 +985,Albertina Oliva,2020-10-15 18:38,Bloody Mary,57 +986,Charmain Brewse,2020-10-13 23:08,Black Magic Julep,69 +987,Geri Sheppard,2020-10-15 20:53,Eclipse,66 +988,Cammy Pitkeathly,2020-10-15 22:20,Black Magic Julep,69 +989,Fremont Fabler,2020-10-15 18:15,Eclipse,66 +990,Judy Aubrey,2020-10-15 23:07,Eclipse,66 +991,Yam Mesicka,2020-10-15 20:53,Eclipse,66 +992,Townie Mulliss,2020-10-15 19:10,Eclipse,66 +993,Ronit Feldman,2020-10-15 19:50,Eclipse,66 +994,Kenton Benzie,2020-10-15 20:38,Eclipse,66 +995,Alister Becker,2020-10-15 21:21,Eclipse,66 +996,Nealon McGiffin,2020-10-15 20:39,Eclipse,66 +997,Judith Plante,2020-10-15 20:51,Eclipse,66 +998,Marijn Breewood,2020-10-15 20:03,Eclipse,66 +999,Jasper Knibley,2020-10-15 21:39,Eclipse,66 +1000,Pace Pickover,2020-10-15 21:14,Truffle Sazerac,64 diff --git a/week13/resources/restaurant.db b/week13/resources/restaurant.db new file mode 100644 index 0000000..bbb995a Binary files /dev/null and b/week13/resources/restaurant.db differ diff --git a/week6/5_Summary.ipynb b/week6/5_Summary.ipynb deleted file mode 100644 index 46fe680..0000000 --- a/week6/5_Summary.ipynb +++ /dev/null @@ -1,1149 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# תרגילים" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### group_by" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - " כתבו פונקציה בשם group_by שמקבלת פונקציה כפרמטר ראשון, ו־iterable כפרמטר שני.
\n", - " הפונקציה תחזיר מילון, שבו:\n", - "

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - "לדוגמה, עבור הקריאה group_by(len, [\"hi\", \"bye\", \"yo\", \"try\"]) יוחזר הערך: {2: [\"hi\", \"yo\"], 3: [\"bye\", \"try\"]}.\n", - "

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### zipwith" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - " כתבו פונקציה בשם zip_with שמקבלת פונקציה כפרמטר ראשון, ושני iterable־ים או יותר בפרמטרים שאחריו.
\n", - " הפונקציה תחזיר רשימה, שבה האיבר במקום ה־N־י הוא הערך שחזר מהעברת כל הערכים במקום ה־N־י של כל ה־iterables לפונקציה.\n", - "

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - "לדוגמה:\n", - "

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - " אפשר להניח שה־iterables המועברים לפונקציה זהים באורכם.\n", - "

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### זכרתם?\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - " כתבו פונקציה שמקבלת מסר להצפנה, ויוצרת ממנו תמונה מוצפנת.
\n", - " השתמשו בשיטת ההצפנה שהוצגה במחברת הקודמת. \n", - "

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### סט" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - " למדו את החוקים של המשחק סט, מהערך בוויקיפדיה או מ־YouTube.\n", - "

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
    \n", - "
  1. צרו חפיסת סט.
  2. \n", - "
  3. טרפו אותה היטב, ופתחו 12 קלפים על השולחן. הדפיסו את כל הסטים שמצאתם.
  4. \n", - "
  5. בדקו בכמה אחוזים מהפעמים שבהן פותחים 12 קלפים אקראיים מהחפיסה – אין אף סט על הלוח.
  6. \n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - " כדי לחשב את סעיף 3, הריצו את הבדיקה על 10,000 מקרים שבהם פתחתם 12 קלפים מהחפיסה המעורבבת.\n", - "

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 100 מעלות" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - " כתבו קוד שמוצא את 100 השירים הפופולריים ביותר לפי מדד Hot 100 של Billboard.
\n", - " השיגו את המילים של השירים שמצאתם, ושרטטו גרף שמראה כמה פעמים מופיעה כל מילה מ־100 המילים הנפוצות ביותר בכל השירים.\n", - "

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - " בונוס: בצעו ניתוח מעניין אחר, כמו מיהם האומנים שמשתמשים בהכי הרבה מילים בשירים שלהם!\n", - "

" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "mode": "lines+markers", - "name": "concat_with_join", - "type": "scatter", - "x": [ - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000 - ], - "y": [ - 1.3000000080864993e-06, - 9.000000318337698e-07, - 5.29999999798747e-06, - 1.1800000038419967e-05, - 8.780000007391209e-05, - 0.0008571000000756612, - 0.010898600000018632 - ] - }, - { - "mode": "lines+markers", - "name": "concat_with_plusequal", - "type": "scatter", - "x": [ - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000 - ], - "y": [ - 8.99999918146932e-07, - 1.4999999393694452e-06, - 1.4800000030845695e-05, - 0.00015469999993911188, - 0.001111600000058388, - 0.010942500000055588, - 1.2957856000000447 - ] - } - ], - "layout": { - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Concatination time comparison" - }, - "xaxis": { - "title": { - "text": "Number of strings" - }, - "type": "log" - }, - "yaxis": { - "title": { - "text": "Elapsed time" - }, - "type": "log" - } - } - }, - "text/html": [ - "
\n", - " \n", - " \n", - "
\n", - " \n", - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "ename": "IndentationError", - "evalue": "expected an indented block (, line 4)", - "output_type": "error", - "traceback": [ - "\u001b[1;36m File \u001b[1;32m\"\"\u001b[1;36m, line \u001b[1;32m4\u001b[0m\n\u001b[1;33m def timecheck2_concat_with_plusequal_nolist(strings):\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mIndentationError\u001b[0m\u001b[1;31m:\u001b[0m expected an indented block\n" - ] - } - ], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -}