דילוג לתוכן המרכזי

מתכונת 4 במדעי המחשב - תכנות באינטרנט 899372

נבחנים אקסטרניים · שאלון 899372 · 9 שאלות · 2 שעות · פתרון מלא לכל סעיף

מבחן מתכונת במדעי המחשב, תכנות באינטרנט, במבנה של בחינת הבגרות. כל שאלה בו בנויה כמו שאלה מבגרות אחרת. לכל סעיף יש תשובה ופתרון מלא, וכל קוד מופיע ב-Java וב-C#. פתרו בזמן של הבחינה, ורק אחר כך פתחו את הפתרונות.

הוראות הבחינה

א. משך הבחינה: שעתיים.

ב. מבנה השאלון ומפתח הערכה: בשאלון זה שלושה פרקים.

פרק ראשון — (10x3) — 30 נקודות
פרק שני — (20x2)+(10x1) — 50 נקודות
פרק שלישי — (20x1) — 20 נקודות
סך הכול — 100 נקודות

ג. חומר עזר מותר בשימוש: כל חומר עזר, חוץ ממחשבון שיש בו אפשרות תכנות.

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

השאלות בשאלון זה מנוסחות בלשון רבים, אף על פי כן על כל תלמידה וכל תלמיד להשיב עליהן באופן אישי.

בהצלחה!

פרק ראשון

ענו על שלוש השאלות 3-1 (לכל שאלה – 10 נקודות).

שאלה 1 · משמעות התכונה runat="server"

בדף aspx מופיעה התגית:

HTML
<div id="msg" runat="server"></div>

מהי משמעות התכונה runat="server" בתגית זו?

א. תוכן התגית div יוצג רק בשרת, ולא יישלח לדפדפן של הגולש
ב. השרת מכיר את התגית כאובייקט, וקוד צד השרת יכול לפנות אליה לפי ה-id שלה ולשנות את תוכנה לפני שהדף נשלח לדפדפן
ג. בלי התכונה הזו קוד JavaScript בדפדפן לא יוכל לגשת לתגית div
ד. התוכן שיוצג בתגית div יישמר במסד הנתונים של האתר

מהי משמעות התכונה runat="server" בתגית <div id="msg" runat="server"></div>?

(10 נקודות)
◂ פתרון מלא לשאלה 1

פתרון

תשובה

ב. השרת מכיר את התגית כאובייקט, וקוד צד השרת יכול לפנות אליה לפי ה-id שלה ולשנות את תוכנה לפני שהדף נשלח לדפדפן

מה נדרש – לבחור את התיאור הנכון של התכונה runat="server".

רעיון – דף aspx מורץ קודם בשרת, ורק התוצאה (HTML רגיל) נשלחת לדפדפן. תגית שמסומנת ב-runat="server" הופכת בשרת לאובייקט, שאפשר לפנות אליו בקוד C# לפי ה-id שלו. למשל, בפעולה Page_Load:

C#
msg.InnerHtml = "Hello " + userName;

השרת מכניס את הטקסט לתוך ה-div, ואז שולח לדפדפן <div id="msg">Hello Noa</div> – התכונה runat עצמה לא מגיעה לדפדפן.

פתרון – התשובה הנכונה: ב.

בדיקה – למה האחרות שגויות:

•א – ה-div כן נשלח לדפדפן ומוצג בו; runat רק מאפשר לשרת לטפל בו לפני השליחה.
•ג – JavaScript רץ בדפדפן ומגיע לתגית לפי ה-id שלה (document.getElementById("msg")) גם בלי runat.
•ד – runat אינו קשור למסד הנתונים; שמירה במסד נעשית בפקודות SQL.

דוגמה: ה-div מקבל בקוד השרת את התוכן "Hello Noa", וה-HTML שנוצר הוא <div id="msg">Hello Noa</div> – עם התוכן החדש ובלי התכונה runat.

טעות נפוצה: לחשוב ש-runat="server" גורם לתגית "להישאר בשרת". היא נשלחת לדפדפן כרגיל – ההבדל הוא שקוד צד השרת יכול לגשת אליה.

שאלה 2 · שאילתה על טבלת סרטים

במסד נתונים של ספריית סרטים מוגדרת טבלה בשם Movies ובה פרטי הסרטים. בטבלה יש ארבע עמודות:

•שם הסרט (title)
•סוגת הסרט (genre), למשל: 'קומדיה', 'אנימציה', 'מתח'
•שנת היציאה לאקרנים (releaseYear)
•אורך הסרט בדקות (minutes)

כתבו שאילתה בשפת SQL המאחזרת את השמות ואת שנות היציאה של סרטי הקומדיה וסרטי האנימציה שאורכם קצר מ-100 דקות.

כתבו שאילתה בשפת SQL המאחזרת את השמות ואת שנות היציאה של סרטי הקומדיה וסרטי האנימציה שאורכם קצר מ-100 דקות.

(10 נקודות)
◂ פתרון מלא לשאלה 2

פתרון

תשובה

SELECT title, releaseYear FROM Movies WHERE (genre = 'קומדיה' OR genre = 'אנימציה') AND minutes < 100;

מה נדרש: שם ושנה של כל סרט שהוא (קומדיה או אנימציה) וגם אורכו פחות מ-100 דקות.

רעיון: סרט אחד לא יכול להיות גם קומדיה וגם אנימציה, לכן בין שתי הסוגות מחברים ב-OR. את תנאי האורך מחברים ב-AND. מכיוון ש-AND קודם ל-OR (כמו כפל לפני חיבור), חייבים סוגריים סביב תנאי ה-OR.

פתרון:

SQL
SELECT title, releaseYear
FROM Movies
WHERE (genre = 'קומדיה' OR genre = 'אנימציה') AND minutes < 100;

(אפשר גם: WHERE genre IN ('קומדיה', 'אנימציה') AND minutes < 100.)

בדיקה: השאילתה על טבלה לדוגמה:

titlegenrereleaseYearminutesמתאים?
Upאנימציה200996כן
Cocoאנימציה2017105לא (ארוך)
Paddingtonקומדיה201495כן
The Maskקומדיה1994101לא (ארוך)
Jawsמתח1975124לא (סוגה אחרת)
Psychoמתח196099לא (סוגה אחרת, אף שהוא קצר)
Shrekאנימציה200190כן

התקבלו Up (2009), Paddington (2014) ו-Shrek (2001).

טעות נפוצה: לכתוב WHERE genre = 'קומדיה' OR genre = 'אנימציה' AND minutes < 100 בלי סוגריים. המשמעות היא "קומדיה (בכל אורך) או (אנימציה קצרה)" – על אותה טבלה מתקבל גם The Mask (101 דקות), שלא היה צריך להופיע. טעות נוספת: genre = 'קומדיה' AND genre = 'אנימציה' – תנאי שאף שורה לא מקיימת.

שאלה 3 · Response.Write של טבלת שאריות עם כותרות

לפניכם הפונקציה Remainders(), רשמו מה יוצג על הדפדפן לאחר הרצת הפונקציה

C#
public void Remainders()
{
  string str = "<table border = '1'>";

  str += "<tr><th>%</th>";
  for (int j = 2;  j <= 5;  j++)
     str += "<th>" + j + "</th>";
  str += "</tr>";

  for (int i = 6;  i <= 10;  i++)
  {
     str += "<tr><th>" + i + "</th>";
     for (int j = 2;  j <= 5;  j++)
     {
        if (i % j == 0)
           str += "<td>V</td>";
        else
           str += "<td>" + i % j + "</td>";
     }
     str += "</tr>";
  }
  str += "</table>";
  Response.Write(str);
}

לפניכם הפונקציה Remainders(), רשמו מה יוצג על הדפדפן לאחר הרצת הפונקציה.

(10 נקודות)
◂ פתרון מלא לשאלה 3

פתרון

תשובה

טבלה עם מסגרת, 6 שורות ו-5 עמודות. השורה הראשונה והעמודה הראשונה הן תאי כותרת (th, מודגשים): % 2 3 4 5 ו-6..10. בתא של שורה i ועמודה j מופיעה השארית i % j, או V כשהשארית 0:
6: V V 2 1 / 7: 1 1 3 2 / 8: V 2 V 3 / 9: 1 V 1 4 / 10: V 1 2 V

מה נדרש – לכתוב מה הדפדפן מציג אחרי ש-Remainders() בונה טבלת HTML ושולחת אותה ב-Response.Write.

רעיון

•לפני הלולאה הכפולה נבנית שורת כותרת: תא <th> עם התו %, ואחריו תאי <th> עם 2, 3, 4, 5. תא <th> הוא תא כותרת – הדפדפן מציג אותו בכתב מודגש וממורכז.
•הלולאה החיצונית (i מ-6 עד 10) בונה 5 שורות. כל שורה מתחילה בתא כותרת <th> עם i.
•הלולאה הפנימית (j מ-2 עד 5) מוסיפה 4 תאים: אם i מתחלק ב-j ללא שארית נכתב V, אחרת נכתבת השארית i % j (ב-C# הפעולה % קודמת לחיבור מחרוזות, לכן נכתב מספר ולא שרשור).

פתרון – מעקב:

ii % 2i % 3i % 4i % 5התאים בשורה
60021V V 2 1
711321 1 3 2
80203V 2 V 3
910141 V 1 4
100120V 1 2 V

המחרוזת שנשלחת לדפדפן:

HTML
<table border = '1'><tr><th>%</th><th>2</th><th>3</th><th>4</th><th>5</th></tr><tr><th>6</th><td>V</td><td>V</td><td>2</td><td>1</td></tr><tr><th>7</th><td>1</td><td>1</td><td>3</td><td>2</td></tr><tr><th>8</th><td>V</td><td>2</td><td>V</td><td>3</td></tr><tr><th>9</th><td>1</td><td>V</td><td>1</td><td>4</td></tr><tr><th>10</th><td>V</td><td>1</td><td>2</td><td>V</td></tr></table>

מה יוצג בדפדפן – טבלה עם מסגרת, 6 שורות ו-5 עמודות (התאים המודגשים הם תאי <th>):

%2345
6VV21
71132
8V2V3
91V14
10V12V

בדיקה – מתקבלת בדיוק המחרוזת שלמעלה: 6 תגיות <tr>, 10 תאי <th> ו-20 תאי <td>.

טעות נפוצה: לכתוב 0 בתאים שבהם השארית 0 (שם נכתב V), או לשכוח את שורת הכותרת ואת תא הכותרת בתחילת כל שורה.

פרק שני

ענו על שלוש שאלות: על שאלה 4 (חובה – 10 נקודות) ועל שתיים מן השאלות 7-5 (לכל שאלה – 20 נקודות).

שאלה 4 · בדיקת חוזק סיסמה ב-JavaScript

בטופס הרשמה לאתר של ספרייה עירונית ישנו שדה למילוי עבור הסיסמה, הרשום בצד השרת באופן הבא:

HTML
<input type="password" maxlength="16" name="pass" id="pass"/>

לפניכם פונקציה ב-javascript שמבצעת בדיקה האם הסיסמה שהוקלדה חזקה מספיק.

סיסמה חזקה חייבת להכיל לפחות 8 תווים, ובהם לפחות 2 ספרות ולפחות אות גדולה אחת באנגלית (A–Z).

לדוגמה: Galil2026 – חזקה; galil2026 – לא חזקה (אין אות גדולה); Galil2 – לא חזקה (קצרה מדי).

בפונקציה חסרים חמישה ביטויים המסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (5), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.

JavaScript
function isStrongPassword()
{
  var x = document.getElementById(____(1)____).value;
  var digits = 0;
  var upper = 0;
  if (x.length < ____(2)____)
     return false;
  for (var i=0; i<x.length; i++)
  {
     var c = x.charAt(i);
     if (c >= '0' && c <= '9')
        ____(3)____;
     if (____(4)_______)
        upper++;
  }
  return ____(5)________;
}

בפונקציה חסרים חמישה ביטויים המסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (5), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.

(10 נקודות)
◂ פתרון מלא לשאלה 4

פתרון

תשובה

(1) "pass"
(2) 8
(3) digits++
(4) c >= 'A' && c <= 'Z'
(5) digits >= 2 && upper >= 1

מה נדרש – להשלים 5 ביטויים בפונקציה שמחזירה true רק אם בשדה pass יש לפחות 8 תווים, מתוכם לפחות 2 ספרות ולפחות אות גדולה אחת.

רעיון – לקרוא את הערך לפי ה-id, לפסול מיד סיסמה קצרה מ-8 תווים, ואז לעבור על כל התווים עם שני מונים: digits סופר ספרות ו-upper סופר אותיות גדולות. בסוף מחזירים את התנאי על שני המונים.

פתרון

מספרהביטוי החסרהסבר
(1)"pass"ה-id של שדה הסיסמה, במירכאות.
(2)8x.length < 8 – פחות מ-8 תווים ⇐ לא חזקה (8 בדיוק מותר).
(3)digits++התו c הוא ספרה ⇐ מגדילים את מונה הספרות.
(4)c >= 'A' && c <= 'Z'התו c הוא אות גדולה באנגלית.
(5)digits >= 2 && upper >= 1שני התנאים חייבים להתקיים יחד.

גם ביטויים שקולים מתקבלים, למשל digits = digits + 1 ב-(3), ו-digits > 1 && upper > 0 ב-(5).

הפונקציה המלאה:

JavaScript
function isStrongPassword()
{
  var x = document.getElementById("pass").value;
  var digits = 0;
  var upper = 0;
  if (x.length < 8)
     return false;
  for (var i=0; i<x.length; i++)
  {
     var c = x.charAt(i);
     if (c >= '0' && c <= '9')
        digits++;
     if (c >= 'A' && c <= 'Z')
        upper++;
  }
  return digits >= 2 && upper >= 1;
}

בדיקה:

קלטספרותאותיות גדולותתוצאה
Galil202641true
Ab1cdef2 (8 תווים בדיוק)21true
galil202640false
Ab1cdefg11false
GALILGALIL010false
1234567880false
Ab1cde2 (7 תווים)––false (נפסלת באורך)

בנוסף בדקנו את הפונקציה מול ההגדרה על כל המחרוזות מעל {1, A, b} עד אורך 10 (88,573 מחרוזות) – התוצאה נכונה בכולן.

טעות נפוצה: להחזיר true כבר בתוך הלולאה ברגע שנמצאה ספרה או אות גדולה – צריך לסיים לספור את כל התווים ורק אז להחליט. טעות נוספת: לכתוב ב-(2) 9 או ב-(5) digits > 2 – אז סיסמה עם 8 תווים בדיוק או עם 2 ספרות בדיוק תיפסל.

שאלה 5 · Session – מסלול מנוי והפניה לדף

באתר מכון הכושר ״כושר בכיף״ אנו רוצים להציג לכל מנוי את המסלול המתאים לו. לשם כך אנו משתמשים במספר משתני Session:

•Session["visits"]: מספר שלם המייצג את מספר האימונים של המנוי בחודש האחרון.
•Session["months"]: מספר שלם המייצג את הוותק של המנוי במכון, בחודשים.
•Session["track"]: מחרוזת המייצגת את המסלול של המנוי ("gold", "silver", "regular").

בהתבסס על משתנים אלה, אנו מעדכנים את המשתנה track ומפנים את המנוי לדף המתאים לו:

א. מנוי שהתאמן 20 פעמים ומעלה בחודש האחרון יעודכן למסלול gold ויועבר לדף vip.aspx
ב. מנוי שהתאמן 8 פעמים ומעלה בחודש האחרון, או שהוא מנוי במכון יותר מ-12 חודשים, יעודכן למסלול silver ויועבר לדף bonus.aspx
ג. שאר המנויים יעודכנו למסלול regular ויועברו לדף plans.aspx

כתבו קוד בצד השרת המתאים לדרישות.

כתבו קוד בצד השרת המתאים לדרישות.

(20 נקודות)
◂ פתרון מלא לשאלה 5

פתרון

תשובה

קוראים את Session["visits"] ו-Session["months"] (המרה ל-int), ואז if / else if / else: visits >= 20 ⇐ gold + vip.aspx; אחרת visits >= 8 || months > 12 ⇐ silver + bonus.aspx; אחרת regular + plans.aspx.

מה נדרש: קוד צד-שרת ב-C# (למשל בתוך Page_Load) שקורא את משתני ה-Session, קובע את המסלול, שומר אותו ב-Session["track"] ומפנה את המנוי לדף המתאים.

רעיון:

1.ערכים ב-Session נשמרים מטיפוס object. כדי להשוות אותם למספרים ממירים ל-int: (int)Session["visits"].
2.שלושת המקרים מוציאים זה את זה ונבדקים לפי הסדר, ולכן מבנה if – else if – else. קודם gold (20 אימונים ומעלה), אחר כך silver, ואחרת regular.

הסדר חשוב: מנוי עם 25 אימונים מקיים גם את התנאי "8 ומעלה", אבל צריך לקבל gold – לכן התנאי של gold נבדק ראשון.

3.בתנאי של silver מספיק שאחד משני התנאים יתקיים ⇐ או ⇐ ||. "ומעלה" ⇐ >=, "יותר מ-12 חודשים" ⇐ > 12 (12 בדיוק לא מספיק).
4.מעדכנים את Session["track"] לפני Response.Redirect, כי ההפניה שולחת את הדפדפן לדף אחר.

פתרון – הקוד המלא בלשונית C#:

C#
protected void Page_Load(object sender, EventArgs e)
{
    int visits = (int)Session["visits"];
    int months = (int)Session["months"];

    if (visits >= 20)
    {
        Session["track"] = "gold";
        Response.Redirect("vip.aspx");
    }
    else if (visits >= 8 || months > 12)
    {
        Session["track"] = "silver";
        Response.Redirect("bonus.aspx");
    }
    else
    {
        Session["track"] = "regular";
        Response.Redirect("plans.aspx");
    }
}

בדיקה:

visitsmonthstrackהפניה
252goldvip.aspx
2030goldvip.aspx
190silverbonus.aspx
81silverbonus.aspx
313silverbonus.aspx
712regularplans.aspx
00regularplans.aspx

כל המקרים, כולל מקרי הגבול (20, 8, 12 חודשים בדיוק), התנהגו לפי הדרישות.

טעות נפוצה: לכתוב && במקום || בתנאי של silver (אז מנוי ותיק שהתאמן מעט לא יקבל silver), או לכתוב months >= 12 – "יותר מ-12" אינו כולל את 12.

הקוד המלא
C#
protected void Page_Load(object sender, EventArgs e)
{
    int visits = (int)Session["visits"];
    int months = (int)Session["months"];

    if (visits >= 20)
    {
        Session["track"] = "gold";
        Response.Redirect("vip.aspx");
    }
    else if (visits >= 8 || months > 12)
    {
        Session["track"] = "silver";
        Response.Redirect("bonus.aspx");
    }
    else
    {
        Session["track"] = "regular";
        Response.Redirect("plans.aspx");
    }
}

שאלה 6 · זוג "גשר"

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

דוגמאות לזוגות מספרים שהם "גשר":

מספר 1מספר 2הסבר
352579הספרה הגדולה ביותר ב-352 היא 5, והספרה הקטנה ביותר ב-579 היא 5
8286הספרה הגדולה ביותר ב-286 היא 8, ו-8 היא הספרה הקטנה ביותר במספר 8
37788הספרה הגדולה ביותר ב-37 היא 7, והספרה הקטנה ביותר ב-788 היא 7

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

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

(20 נקודות)
◂ פתרון מלא לשאלה 6

פתרון

תשובה

return MaxDigit(num1) == MinDigit(num2) || MaxDigit(num2) == MinDigit(num1); (פעולות עזר שמוצאות את הספרה הגדולה/הקטנה ביותר בלולאה עם % 10 ו-/ 10)

מה נדרש: פונקציה בוליאנית שבודקת אם הספרה הגדולה ביותר של אחד המספרים שווה לספרה הקטנה ביותר של המספר האחר.

רעיון:

•שתי פעולות עזר: MaxDigit ו-MinDigit. כל אחת עוברת על ספרות המספר בלולאה (num % 10 – הספרה הימנית, num / 10 – מחיקתה) ומעדכנת את הגדולה/הקטנה ביותר שנמצאה עד כה.
•את ערך ההתחלה של max ו-min לוקחים מספרה של המספר עצמו (ספרת האחדות) ולא 0 או 9 שרירותיים – כך הפעולה נכונה לכל מספר, כולל חד-ספרתי (שבו הספרה היחידה היא גם הגדולה וגם הקטנה, כנדרש).
•"אין חשיבות לסדר" – לכן בודקים את שני הכיוונים: גדולה של num1 מול קטנה של num2, או גדולה של num2 מול קטנה של num1.

פתרון:

C#
// פעולת עזר: מחזירה את הספרה הגדולה ביותר במספר חיובי
public int MaxDigit(int num)
{
    int max = num % 10;
    while (num > 0)
    {
        if (num % 10 > max)
            max = num % 10;
        num = num / 10;
    }
    return max;
}

// פעולת עזר: מחזירה את הספרה הקטנה ביותר במספר חיובי
public int MinDigit(int num)
{
    int min = num % 10;
    while (num > 0)
    {
        if (num % 10 < min)
            min = num % 10;
        num = num / 10;
    }
    return min;
}

public bool IsBridge(int num1, int num2)
{
    return MaxDigit(num1) == MinDigit(num2) || MaxDigit(num2) == MinDigit(num1);
}

בדיקה:

num1num2גדולה/קטנה ב-num1גדולה/קטנה ב-num2תוצאה
3525795 / 29 / 5True (5 = 5)
82868 / 88 / 2True (הגדולה של 286 = 8)
377887 / 38 / 7True (7 = 7)
9099 / 09 / 9True
38858 / 38 / 5False
10515 / 01 / 1False

טעות נפוצה: לבדוק כיוון אחד בלבד – אז הזוג (8, 286) מהדוגמה ייכשל. טעות נוספת: לאתחל min = 0 – אז הספרה הקטנה ביותר תצא תמיד 0.

הקוד המלא
C#
// פעולת עזר: מחזירה את הספרה הגדולה ביותר במספר חיובי
public int MaxDigit(int num)
{
    int max = num % 10;
    while (num > 0)
    {
        if (num % 10 > max)
            max = num % 10;
        num = num / 10;
    }
    return max;
}

// פעולת עזר: מחזירה את הספרה הקטנה ביותר במספר חיובי
public int MinDigit(int num)
{
    int min = num % 10;
    while (num > 0)
    {
        if (num % 10 < min)
            min = num % 10;
        num = num / 10;
    }
    return min;
}

public bool IsBridge(int num1, int num2)
{
    return MaxDigit(num1) == MinDigit(num2) || MaxDigit(num2) == MinDigit(num1);
}

שאלה 7 · SQL – שאילתה עם כמה תנאים (OR)

במסד נתונים של ספרייה עירונית יש טבלה ששמה BooksTable המכילה את נתוני הספרים בספרייה.
העמודות בטבלה הן:

א. title (שם הספר), מסוג טקסט.
ב. author (שם הסופר), מסוג טקסט.
ג. year (שנת ההוצאה לאור), מסוג מספר שלם.
ד. copies (מספר העותקים של הספר בספרייה), מסוג מספר שלם.
ה. borrowed (מספר העותקים המושאלים כעת), מסוג מספר שלם.

דוגמה לטבלה ובה כמה מן הספרים בספרייה:

titleauthoryearcopiesborrowed
מסע אל הכוכביםדוד לב198532
גן הסודותנועה שחר199040
ימי החורףרון אלון201251
הספינה האחרונהמיכל בר197563
שירי ערשמיכל בר194820
העיר הלבנהיעל דגן200031
אור בחלוןרון אלון199922

כתבו שאילתה אחת בשפת SQL המבקשת להציג את שמות הספרים המקיימים אחד או יותר מהתנאים הבאים:

א. בספרייה נותרו פחות משני עותקים פנויים (שאינם מושאלים) של הספר.
ב. הספר יצא לאור לפני שנת 1950 או אחרי שנת 2000.
ג. את הספר כתבה הסופרת נועה שחר.

(20 נקודות)
◂ פתרון מלא לשאלה 7

פתרון

תשובה

SELECT title FROM BooksTable WHERE copies - borrowed < 2 OR year < 1950 OR year > 2000 OR author = 'נועה שחר';
(על טבלת הדוגמה: מסע אל הכוכבים, גן הסודות, ימי החורף, שירי ערש, אור בחלון)

מה נדרש – שאילתה אחת שמחזירה את שמות הספרים שמקיימים לפחות אחד מהתנאים ⇒ חיבור ב־OR.

רעיון

•א: מספר העותקים הפנויים מחושב בתנאי: copies - borrowed. "פחות משניים" ⇒ copies - borrowed < 2.
•ב: "לפני 1950 או אחרי 2000" ⇒ year < 1950 OR year > 2000 (שנת 1950 ושנת 2000 עצמן אינן נכללות). אפשר לכתוב באופן שקול year NOT BETWEEN 1950 AND 2000.
•ג: author = 'נועה שחר' – טקסט בין גרשיים בודדים.
•כל התנאים מחוברים ב־OR, ולכן אין צורך בסוגריים.

פתרון

SQL
SELECT title
FROM BooksTable
WHERE copies - borrowed < 2
   OR year < 1950 OR year > 2000
   OR author = 'נועה שחר';

בדיקה – על טבלת הדוגמה:

titleא: copies − borrowed < 2ב: year < 1950 או year > 2000ג: נועה שחרמוצג?
מסע אל הכוכבים1 ✔1985 ✘✘כן
גן הסודות4 ✘1990 ✘✔כן
ימי החורף4 ✘2012 ✔✘כן
הספינה האחרונה3 ✘1975 ✘✘לא
שירי ערש2 ✘1948 ✔✘כן
העיר הלבנה2 ✘2000 ✘✘לא
אור בחלון0 ✔1999 ✘✘כן

התוצאה: מסע אל הכוכבים, גן הסודות, ימי החורף, שירי ערש, אור בחלון. (גם הגרסה עם NOT BETWEEN החזירה בדיוק את אותם ספרים.)

טעות נפוצה: לתרגם את תנאי ב ל־year < 1950 AND year > 2000 – אין שנה שהיא גם לפני 1950 וגם אחרי 2000, ולכן התנאי לעולם אינו מתקיים (ימי החורף ושירי ערש נעלמים מהתוצאה). טעות נוספת: copies - borrowed <= 2 – אז גם "שירי ערש" ו"העיר הלבנה" (2 עותקים פנויים בדיוק) ייחשבו בטעות.

פרק שלישי

ענו על שאלה אחת מן השאלות 9-8.

שאלה 8 · הרשמה לרשימת תפוצה – בדיקת כפילות ב-DataSet

מועדון טיולים שולח לחבריו עדכונים בדואר אלקטרוני. בטופס ההרשמה לרשימת התפוצה מקלידים כתובת דואר אלקטרוני ושם מלא:

HTML
<input type="text" name="email" id="email" />

<input type="text" name="fullName" id="fullName" />

בבסיס הנתונים קיימת טבלה בשם subscribers ובה שתי עמודות:

•email – משתנה מסוג טקסט
•fullName – משתנה מסוג טקסט

דוגמה לטבלה:

emailfullName
dana@mail.comDana Shalev
yossi@mail.comYossi Katz

כל כתובת דואר אלקטרוני יכולה להופיע בטבלה פעם אחת לכל היותר (שתי כתובות נחשבות זהות רק אם המחרוזות שלהן זהות בדיוק):

•אם הכתובת שהוקלדה כבר קיימת בטבלה, אין להוסיף שורה, ויש להציג את ההודעה: כתובת זו כבר רשומה
•אחרת, יש להוסיף לטבלה את הנרשם החדש ולהציג את ההודעה: נרשמת בהצלחה

לפניכם קטע קוד חלקי תקין של פעולה בשם Subscribe. השלימו את הקוד החסר.

C#
protected void Subscribe(string connectionString)
{
    SqlConnection connection = new SqlConnection(connectionString);
    SqlCommand command = new SqlCommand();
    command.CommandText = "Select * From subscribers";
    command.Connection = connection;
    connection.Open();
    SqlDataAdapter adapter = new SqlDataAdapter(command);
    SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
    DataSet ds = new DataSet();
    adapter.Fill(ds);
    string email = Request.Form["email"];
    string fullName = Request.Form["fullName"];

            ************************************
            ************************************

    adapter.Update(ds);
    connection.Close();
}

יש להשלים את הקוד החסר (מסומן בכוכביות).

יש להשלים את הקוד החסר (מסומן בכוכביות).

(20 נקודות)
◂ פתרון מלא לשאלה 8

פתרון

תשובה
C#
bool exists = false;
foreach (DataRow r in ds.Tables[0].Rows)
{
    if (r["email"].ToString() == email)
        exists = true;
}
if (exists)
{
    Response.Write("כתובת זו כבר רשומה");
}
else
{
    DataRow row = ds.Tables[0].NewRow();
    row["email"] = email;
    row["fullName"] = fullName;
    ds.Tables[0].Rows.Add(row);
    Response.Write("נרשמת בהצלחה");
}

מה נדרש: לפני ההוספה לבדוק אם הכתובת כבר קיימת בטבלה. אם כן – רק להציג הודעה; אם לא – להוסיף שורה חדשה ולהציג הודעת הצלחה.

רעיון: אחרי adapter.Fill(ds) כל השורות של subscribers נמצאות ב-ds.Tables[0].Rows.

1.חיפוש: משתנה בוליאני exists = false, ולולאת foreach על כל השורות: אם r["email"].ToString() שווה לכתובת מהטופס ⇐ exists = true. (‏r["email"] הוא object, לכן ממירים למחרוזת לפני ההשוואה.)
2.החלטה: אם exists – Response.Write("כתובת זו כבר רשומה") ולא משנים את הטבלה. אחרת – NewRow(), ממלאים את שתי העמודות, Rows.Add(row) ומציגים "נרשמת בהצלחה".
3.adapter.Update(ds) שבסוף הפעולה שומר במסד רק שורות שנוספו; אם לא הוספנו דבר – אין שינוי במסד. ה-SqlCommandBuilder כבר נמצא בקוד הנתון.

פתרון – הקוד החסר (במקום הכוכביות):

C#
bool exists = false;
foreach (DataRow r in ds.Tables[0].Rows)
{
    if (r["email"].ToString() == email)
        exists = true;
}
if (exists)
{
    Response.Write("כתובת זו כבר רשומה");
}
else
{
    DataRow row = ds.Tables[0].NewRow();
    row["email"] = email;
    row["fullName"] = fullName;
    ds.Tables[0].Rows.Add(row);
    Response.Write("נרשמת בהצלחה");
}

הפעולה המלאה – בלשונית C#.

בדיקה: טבלה עם dana@mail.com ו-yossi@mail.com, בארבע קריאות ברצף:

email בטופסהודעהשורות בטבלה
lior@mail.comנרשמת בהצלחה3
yossi@mail.comכתובת זו כבר רשומה3
lior@mail.comכתובת זו כבר רשומה3
noa@mail.comנרשמת בהצלחה4

הקריאה השלישית מראה שגם כתובת שנוספה רק עכשיו (lior) מזוהה ככפולה.

טעות נפוצה: לכתוב את ההוספה בתוך הלולאה (else של ה-if שבלולאה) – אז נוספת שורה עבור כל שורה שאינה שווה לכתובת. את ההחלטה מקבלים רק אחרי שהלולאה עברה על כל השורות. וגם: להשוות r["email"] == email בלי ToString() – זו השוואה של object למחרוזת, שאינה משווה את התוכן.

הקוד המלא
C#
protected void Subscribe(string connectionString)
{
    SqlConnection connection = new SqlConnection(connectionString);
    SqlCommand command = new SqlCommand();
    command.CommandText = "Select * From subscribers";
    command.Connection = connection;
    connection.Open();
    SqlDataAdapter adapter = new SqlDataAdapter(command);
    SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
    DataSet ds = new DataSet();
    adapter.Fill(ds);
    string email = Request.Form["email"];
    string fullName = Request.Form["fullName"];

    // ---- הקוד החסר ----
    bool exists = false;
    foreach (DataRow r in ds.Tables[0].Rows)
    {
        if (r["email"].ToString() == email)
            exists = true;
    }
    if (exists)
    {
        Response.Write("כתובת זו כבר רשומה");
    }
    else
    {
        DataRow row = ds.Tables[0].NewRow();
        row["email"] = email;
        row["fullName"] = fullName;
        ds.Tables[0].Rows.Add(row);
        Response.Write("נרשמת בהצלחה");
    }
    // --------------------

    adapter.Update(ds);
    connection.Close();
}

שאלה 9 · השלמת פעולה המציגה עובדים ממסד הנתונים

במסד הנתונים של חברת תוכנה יש טבלה בשם EmployeesTable ובה ארבע עמודות: name (שם העובד, טקסט), department (מחלקה, טקסט), salary (שכר חודשי, מספר שלם) ו־years (ותק בשנים, מספר שלם).

דוגמה לטבלה:

namedepartmentsalaryyears
אבימכירות90003
בתיהכספים1200012
גילפיתוח150004
דנהמכירות950010
הדרפיתוח1400010
ורדכספים110009

מנהלת משאבי האנוש מעוניינת להפיק טבלה ובה כל עובד המקיים לפחות אחד משני התנאים האלה: העובד שייך למחלקת המכירות, או שהוותק שלו 10 שנים לפחות (בכל מחלקה). עובד המקיים את שני התנאים יופיע בטבלה פעם אחת. הפעולה showEmployees מקבלת מחרוזת התחברות למסד הנתונים connectionString, ומציגה בטבלה את השם, המחלקה והוותק של העובדים המתאימים.

בפעולה חסרים שמונה ביטויים המסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (8), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.

C#
protected void showEmployees(string connectionString)
{
    string resultStr = "";
    DataSet ds = new DataSet();
    SqlConnection connection = _____(1)_____;
    _____(2)_____ cmd = new SqlCommand();
    cmd.CommandText = "SELECT * FROM EmployeesTable WHERE _____(3)_____";
    cmd.Connection = connection;
    connection.Open();
    SqlDataAdapter adapter = new SqlDataAdapter(cmd);
    adapter.Fill(_____(4)_____);
    connection._____(5)_____;
    resultStr += "<table dir=rtl><tr><th>שם</th><th>מחלקה</th><th>ותק</th></tr>";

    foreach (DataRow dr in ds.Tables[0].Rows)
    {
        resultStr += "_____(6)_____";
        resultStr += "<td>" + dr["name"] + "</td>";
        resultStr += "<td>" + dr[_____(7)_____] + "</td>";
        resultStr += "<td>" + dr["years"] + "</td>";
        resultStr += "</tr>";
    }
    resultStr += "_____(8)_____";
    Response.Write(resultStr);
}

כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (8), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.

(20 נקודות)
◂ פתרון מלא לשאלה 9

פתרון

תשובה

(1) new SqlConnection(connectionString)
(2) SqlCommand
(3) department = 'מכירות' OR years >= 10
(4) ds
(5) Close()
(6) <tr>
(7) "department"
(8) </table>

מה נדרש – להשלים 8 ביטויים חסרים בפעולה שמתחברת למסד הנתונים, שולפת את השורות המתאימות ומציגה אותן בטבלת HTML.

רעיון – התבנית הקבועה של קריאה ממסד נתונים ב־ASP.NET:
יצירת DataSet → חיבור (SqlConnection עם מחרוזת ההתחברות) → פקודה (SqlCommand עם טקסט השאילתה והחיבור) → פתיחת החיבור → SqlDataAdapter שמקבל את הפקודה וממלא (Fill) את ה־DataSet → סגירת החיבור → מעבר על השורות של ds.Tables[0] ובניית מחרוזת HTML: שורה <tr>...</tr> לכל רשומה ותא <td>...</td> לכל ערך → שליחה לדפדפן עם Response.Write.

פתרון

מספרהביטוי החסרהסבר
(1)new SqlConnection(connectionString)יצירת החיבור למסד עם מחרוזת ההתחברות שהפעולה קיבלה
(2)SqlCommandהטיפוס של המשתנה cmd – פקודת SQL
(3)department = 'מכירות' OR years >= 10עובד מתאים אם הוא במחלקת המכירות או שהוותק שלו 10 לפחות ⇒ OR; "לפחות" ⇒ >=
(4)dsה־adapter ממלא את ה־DataSet ששמו ds
(5)Close()סגירת החיבור אחרי שהנתונים נטענו
(6)<tr>פתיחת שורה בטבלה; היא נסגרת ב־</tr> בסוף האיטרציה
(7)"department"שם העמודה של המחלקה, כמחרוזת בין מירכאות
(8)</table>סגירת הטבלה אחרי הלולאה

הפעולה המלאה אחרי ההשלמה:

C#
protected void showEmployees(string connectionString)
{
    string resultStr = "";
    DataSet ds = new DataSet();
    SqlConnection connection = new SqlConnection(connectionString);
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = "SELECT * FROM EmployeesTable WHERE department = 'מכירות' OR years >= 10";
    cmd.Connection = connection;
    connection.Open();
    SqlDataAdapter adapter = new SqlDataAdapter(cmd);
    adapter.Fill(ds);
    connection.Close();
    resultStr += "<table dir=rtl><tr><th>שם</th><th>מחלקה</th><th>ותק</th></tr>";

    foreach (DataRow dr in ds.Tables[0].Rows)
    {
        resultStr += "<tr>";
        resultStr += "<td>" + dr["name"] + "</td>";
        resultStr += "<td>" + dr["department"] + "</td>";
        resultStr += "<td>" + dr["years"] + "</td>";
        resultStr += "</tr>";
    }
    resultStr += "</table>";
    Response.Write(resultStr);
}

בדיקה – הפעולה המלאה על נתוני הטבלה מהשאלה. השורות שהוצגו:

namedepartmentyears
אבימכירות3
בתיהכספים12
דנהמכירות10
הדרפיתוח10

גיל (פיתוח, 4 שנים) וורד (כספים, 9 שנים) אינם מקיימים אף אחד מהתנאים. (סדר השורות בתוצאה של שאילתה בלי ORDER BY אינו מובטח; מוחזרים בדיוק ארבעת העובדים האלה.)

טעות נפוצה: ב־(3) לכתוב AND – אבל עובד מוצג אם הוא מקיים לפחות אחד מהתנאים ⇒ OR. עם AND הייתה מוצגת רק דנה (עובדת מכירות עם ותק של 10 שנים). דנה מקיימת את שני התנאים, ובכל זאת השאילתה מחזירה אותה פעם אחת בלבד – כל שורה בטבלה נבדקת פעם אחת. ב־(7) לכתוב dr[department] בלי מירכאות – שגיאת הידור, כי אין משתנה בשם department. ב־(8) לשכוח את </table>.

הקוד המלא
C#
protected void showEmployees(string connectionString)
{
    string resultStr = "";
    DataSet ds = new DataSet();
    SqlConnection connection = new SqlConnection(connectionString);
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = "SELECT * FROM EmployeesTable WHERE department = 'מכירות' OR years >= 10";
    cmd.Connection = connection;
    connection.Open();
    SqlDataAdapter adapter = new SqlDataAdapter(cmd);
    adapter.Fill(ds);
    connection.Close();
    resultStr += "<table dir=rtl><tr><th>שם</th><th>מחלקה</th><th>ותק</th></tr>";

    foreach (DataRow dr in ds.Tables[0].Rows)
    {
        resultStr += "<tr>";
        resultStr += "<td>" + dr["name"] + "</td>";
        resultStr += "<td>" + dr["department"] + "</td>";
        resultStr += "<td>" + dr["years"] + "</td>";
        resultStr += "</tr>";
    }
    resultStr += "</table>";
    Response.Write(resultStr);
}
טבלת התשובות
שאלהסעיףתשובה
1-

ב. השרת מכיר את התגית כאובייקט, וקוד צד השרת יכול לפנות אליה לפי ה-id שלה ולשנות את תוכנה לפני שהדף נשלח לדפדפן

2-

SELECT title, releaseYear FROM Movies WHERE (genre = 'קומדיה' OR genre = 'אנימציה') AND minutes < 100;

3-

טבלה עם מסגרת, 6 שורות ו-5 עמודות. השורה הראשונה והעמודה הראשונה הן תאי כותרת (th, מודגשים): % 2 3 4 5 ו-6..10. בתא של שורה i ועמודה j מופיעה השארית i % j, או V כשהשארית 0:
6: V V 2 1 / 7: 1 1 3 2 / 8: V 2 V 3 / 9: 1 V 1 4 / 10: V 1 2 V

4-

(1) "pass"
(2) 8
(3) digits++
(4) c >= 'A' && c <= 'Z'
(5) digits >= 2 && upper >= 1

5-

קוראים את Session["visits"] ו-Session["months"] (המרה ל-int), ואז if / else if / else: visits >= 20 ⇐ gold + vip.aspx; אחרת visits >= 8 || months > 12 ⇐ silver + bonus.aspx; אחרת regular + plans.aspx.

6-

return MaxDigit(num1) == MinDigit(num2) || MaxDigit(num2) == MinDigit(num1); (פעולות עזר שמוצאות את הספרה הגדולה/הקטנה ביותר בלולאה עם % 10 ו-/ 10)

7-

SELECT title FROM BooksTable WHERE copies - borrowed < 2 OR year < 1950 OR year > 2000 OR author = 'נועה שחר';
(על טבלת הדוגמה: מסע אל הכוכבים, גן הסודות, ימי החורף, שירי ערש, אור בחלון)

8-
C#
bool exists = false;
foreach (DataRow r in ds.Tables[0].Rows)
{
    if (r["email"].ToString() == email)
        exists = true;
}
if (exists)
{
    Response.Write("כתובת זו כבר רשומה");
}
else
{
    DataRow row = ds.Tables[0].NewRow();
    row["email"] = email;
    row["fullName"] = fullName;
    ds.Tables[0].Rows.Add(row);
    Response.Write("נרשמת בהצלחה");
}
9-

(1) new SqlConnection(connectionString)
(2) SqlCommand
(3) department = 'מכירות' OR years >= 10
(4) ds
(5) Close()
(6) <tr>
(7) "department"
(8) </table>

כל האתר במבט אחד

קיצורי דרך לכל הכלים, הבגרויות והקורסים

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