מתכונת 4 במדעי המחשב - תכנות באינטרנט 899372
נבחנים אקסטרניים · שאלון 899372 · 9 שאלות · 2 שעות · פתרון מלא לכל סעיף
מבחן מתכונת במדעי המחשב, תכנות באינטרנט, במבנה של בחינת הבגרות. כל שאלה בו בנויה כמו שאלה מבגרות אחרת. לכל סעיף יש תשובה ופתרון מלא, וכל קוד מופיע ב-Java וב-C#. פתרו בזמן של הבחינה, ורק אחר כך פתחו את הפתרונות.
הוראות הבחינה
א. משך הבחינה: שעתיים.
ב. מבנה השאלון ומפתח הערכה: בשאלון זה שלושה פרקים.
פרק ראשון — (10x3) — 30 נקודות
פרק שני — (20x2)+(10x1) — 50 נקודות
פרק שלישי — (20x1) — 20 נקודות
סך הכול — 100 נקודות
ג. חומר עזר מותר בשימוש: כל חומר עזר, חוץ ממחשבון שיש בו אפשרות תכנות.
יש לכתוב במחברת הבחינה בלבד, יש לרשום "טיוטה" בראש כל עמוד המשמש טיוטה.
כתיבת טיוטה בדפים שאינם במחברת הבחינה עלולה לגרום לפסילת הבחינה.
השאלות בשאלון זה מנוסחות בלשון רבים, אף על פי כן על כל תלמידה וכל תלמיד להשיב עליהן באופן אישי.
בהצלחה!
פרק ראשון
ענו על שלוש השאלות 3-1 (לכל שאלה – 10 נקודות).
שאלה 1 · משמעות התכונה runat="server"
בדף aspx מופיעה התגית:
<div id="msg" runat="server"></div>מהי משמעות התכונה runat="server" בתגית זו?
א. תוכן התגית div יוצג רק בשרת, ולא יישלח לדפדפן של הגולש
ב. השרת מכיר את התגית כאובייקט, וקוד צד השרת יכול לפנות אליה לפי ה-id שלה ולשנות את תוכנה לפני שהדף נשלח לדפדפן
ג. בלי התכונה הזו קוד JavaScript בדפדפן לא יוכל לגשת לתגית div
ד. התוכן שיוצג בתגית div יישמר במסד הנתונים של האתר
מהי משמעות התכונה runat="server" בתגית <div id="msg" runat="server"></div>?
◂ פתרון מלא לשאלה 1
פתרון
ב. השרת מכיר את התגית כאובייקט, וקוד צד השרת יכול לפנות אליה לפי ה-id שלה ולשנות את תוכנה לפני שהדף נשלח לדפדפן
מה נדרש – לבחור את התיאור הנכון של התכונה runat="server".
רעיון – דף aspx מורץ קודם בשרת, ורק התוצאה (HTML רגיל) נשלחת לדפדפן. תגית שמסומנת ב-runat="server" הופכת בשרת לאובייקט, שאפשר לפנות אליו בקוד C# לפי ה-id שלו. למשל, בפעולה Page_Load:
msg.InnerHtml = "Hello " + userName;השרת מכניס את הטקסט לתוך ה-div, ואז שולח לדפדפן <div id="msg">Hello Noa</div> – התכונה runat עצמה לא מגיעה לדפדפן.
פתרון – התשובה הנכונה: ב.
בדיקה – למה האחרות שגויות:
document.getElementById("msg")) גם בלי runat.דוגמה: ה-div מקבל בקוד השרת את התוכן "Hello Noa", וה-HTML שנוצר הוא <div id="msg">Hello Noa</div> – עם התוכן החדש ובלי התכונה runat.
טעות נפוצה: לחשוב ש-runat="server" גורם לתגית "להישאר בשרת". היא נשלחת לדפדפן כרגיל – ההבדל הוא שקוד צד השרת יכול לגשת אליה.
שאלה 2 · שאילתה על טבלת סרטים
במסד נתונים של ספריית סרטים מוגדרת טבלה בשם Movies ובה פרטי הסרטים. בטבלה יש ארבע עמודות:
כתבו שאילתה בשפת SQL המאחזרת את השמות ואת שנות היציאה של סרטי הקומדיה וסרטי האנימציה שאורכם קצר מ-100 דקות.
כתבו שאילתה בשפת SQL המאחזרת את השמות ואת שנות היציאה של סרטי הקומדיה וסרטי האנימציה שאורכם קצר מ-100 דקות.
◂ פתרון מלא לשאלה 2
פתרון
SELECT title, releaseYear FROM Movies WHERE (genre = 'קומדיה' OR genre = 'אנימציה') AND minutes < 100;
מה נדרש: שם ושנה של כל סרט שהוא (קומדיה או אנימציה) וגם אורכו פחות מ-100 דקות.
רעיון: סרט אחד לא יכול להיות גם קומדיה וגם אנימציה, לכן בין שתי הסוגות מחברים ב-OR. את תנאי האורך מחברים ב-AND. מכיוון ש-AND קודם ל-OR (כמו כפל לפני חיבור), חייבים סוגריים סביב תנאי ה-OR.
פתרון:
SELECT title, releaseYear
FROM Movies
WHERE (genre = 'קומדיה' OR genre = 'אנימציה') AND minutes < 100;(אפשר גם: WHERE genre IN ('קומדיה', 'אנימציה') AND minutes < 100.)
בדיקה: השאילתה על טבלה לדוגמה:
| title | genre | releaseYear | minutes | מתאים? |
|---|---|---|---|---|
| Up | אנימציה | 2009 | 96 | כן |
| Coco | אנימציה | 2017 | 105 | לא (ארוך) |
| Paddington | קומדיה | 2014 | 95 | כן |
| The Mask | קומדיה | 1994 | 101 | לא (ארוך) |
| Jaws | מתח | 1975 | 124 | לא (סוגה אחרת) |
| Psycho | מתח | 1960 | 99 | לא (סוגה אחרת, אף שהוא קצר) |
| Shrek | אנימציה | 2001 | 90 | כן |
התקבלו Up (2009), Paddington (2014) ו-Shrek (2001).
טעות נפוצה: לכתוב WHERE genre = 'קומדיה' OR genre = 'אנימציה' AND minutes < 100 בלי סוגריים. המשמעות היא "קומדיה (בכל אורך) או (אנימציה קצרה)" – על אותה טבלה מתקבל גם The Mask (101 דקות), שלא היה צריך להופיע. טעות נוספת: genre = 'קומדיה' AND genre = 'אנימציה' – תנאי שאף שורה לא מקיימת.
שאלה 3 · Response.Write של טבלת שאריות עם כותרות
לפניכם הפונקציה Remainders(), רשמו מה יוצג על הדפדפן לאחר הרצת הפונקציה
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(), רשמו מה יוצג על הדפדפן לאחר הרצת הפונקציה.
◂ פתרון מלא לשאלה 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> הוא תא כותרת – הדפדפן מציג אותו בכתב מודגש וממורכז.<th> עם i.פתרון – מעקב:
| i | i % 2 | i % 3 | i % 4 | i % 5 | התאים בשורה |
|---|---|---|---|---|---|
| 6 | 0 | 0 | 2 | 1 | V V 2 1 |
| 7 | 1 | 1 | 3 | 2 | 1 1 3 2 |
| 8 | 0 | 2 | 0 | 3 | V 2 V 3 |
| 9 | 1 | 0 | 1 | 4 | 1 V 1 4 |
| 10 | 0 | 1 | 2 | 0 | V 1 2 V |
המחרוזת שנשלחת לדפדפן:
<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>):
| % | 2 | 3 | 4 | 5 |
|---|---|---|---|---|
| 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 |
בדיקה – מתקבלת בדיוק המחרוזת שלמעלה: 6 תגיות <tr>, 10 תאי <th> ו-20 תאי <td>.
טעות נפוצה: לכתוב 0 בתאים שבהם השארית 0 (שם נכתב V), או לשכוח את שורת הכותרת ואת תא הכותרת בתחילת כל שורה.
פרק שני
ענו על שלוש שאלות: על שאלה 4 (חובה – 10 נקודות) ועל שתיים מן השאלות 7-5 (לכל שאלה – 20 נקודות).
שאלה 4 · בדיקת חוזק סיסמה ב-JavaScript
בטופס הרשמה לאתר של ספרייה עירונית ישנו שדה למילוי עבור הסיסמה, הרשום בצד השרת באופן הבא:
<input type="password" maxlength="16" name="pass" id="pass"/>לפניכם פונקציה ב-javascript שמבצעת בדיקה האם הסיסמה שהוקלדה חזקה מספיק.
סיסמה חזקה חייבת להכיל לפחות 8 תווים, ובהם לפחות 2 ספרות ולפחות אות גדולה אחת באנגלית (A–Z).
לדוגמה: Galil2026 – חזקה; galil2026 – לא חזקה (אין אות גדולה); Galil2 – לא חזקה (קצרה מדי).
בפונקציה חסרים חמישה ביטויים המסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (5), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
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), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
◂ פתרון מלא לשאלה 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) | 8 | x.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).
הפונקציה המלאה:
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;
}בדיקה:
| קלט | ספרות | אותיות גדולות | תוצאה |
|---|---|---|---|
Galil2026 | 4 | 1 | true |
Ab1cdef2 (8 תווים בדיוק) | 2 | 1 | true |
galil2026 | 4 | 0 | false |
Ab1cdefg | 1 | 1 | false |
GALILGALIL | 0 | 10 | false |
12345678 | 8 | 0 | false |
Ab1cde2 (7 תווים) | – | – | false (נפסלת באורך) |
בנוסף בדקנו את הפונקציה מול ההגדרה על כל המחרוזות מעל {1, A, b} עד אורך 10 (88,573 מחרוזות) – התוצאה נכונה בכולן.
טעות נפוצה: להחזיר true כבר בתוך הלולאה ברגע שנמצאה ספרה או אות גדולה – צריך לסיים לספור את כל התווים ורק אז להחליט. טעות נוספת: לכתוב ב-(2) 9 או ב-(5) digits > 2 – אז סיסמה עם 8 תווים בדיוק או עם 2 ספרות בדיוק תיפסל.
שאלה 5 · Session – מסלול מנוי והפניה לדף
באתר מכון הכושר ״כושר בכיף״ אנו רוצים להציג לכל מנוי את המסלול המתאים לו. לשם כך אנו משתמשים במספר משתני Session:
בהתבסס על משתנים אלה, אנו מעדכנים את המשתנה track ומפנים את המנוי לדף המתאים לו:
א. מנוי שהתאמן 20 פעמים ומעלה בחודש האחרון יעודכן למסלול gold ויועבר לדף vip.aspx
ב. מנוי שהתאמן 8 פעמים ומעלה בחודש האחרון, או שהוא מנוי במכון יותר מ-12 חודשים, יעודכן למסלול silver ויועבר לדף bonus.aspx
ג. שאר המנויים יעודכנו למסלול regular ויועברו לדף plans.aspx
כתבו קוד בצד השרת המתאים לדרישות.
כתבו קוד בצד השרת המתאים לדרישות.
◂ פתרון מלא לשאלה 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"] ומפנה את המנוי לדף המתאים.
רעיון:
object. כדי להשוות אותם למספרים ממירים ל-int: (int)Session["visits"].if – else if – else. קודם gold (20 אימונים ומעלה), אחר כך silver, ואחרת regular.הסדר חשוב: מנוי עם 25 אימונים מקיים גם את התנאי "8 ומעלה", אבל צריך לקבל gold – לכן התנאי של gold נבדק ראשון.
||. "ומעלה" ⇐ >=, "יותר מ-12 חודשים" ⇐ > 12 (12 בדיוק לא מספיק).Session["track"] לפני Response.Redirect, כי ההפניה שולחת את הדפדפן לדף אחר.פתרון – הקוד המלא בלשונית 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");
}
}בדיקה:
| visits | months | track | הפניה |
|---|---|---|---|
| 25 | 2 | gold | vip.aspx |
| 20 | 30 | gold | vip.aspx |
| 19 | 0 | silver | bonus.aspx |
| 8 | 1 | silver | bonus.aspx |
| 3 | 13 | silver | bonus.aspx |
| 7 | 12 | regular | plans.aspx |
| 0 | 0 | regular | plans.aspx |
כל המקרים, כולל מקרי הגבול (20, 8, 12 חודשים בדיוק), התנהגו לפי הדרישות.
טעות נפוצה: לכתוב && במקום || בתנאי של silver (אז מנוי ותיק שהתאמן מעט לא יקבל silver), או לכתוב months >= 12 – "יותר מ-12" אינו כולל את 12.
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 | הסבר |
|---|---|---|
| 352 | 579 | הספרה הגדולה ביותר ב-352 היא 5, והספרה הקטנה ביותר ב-579 היא 5 |
| 8 | 286 | הספרה הגדולה ביותר ב-286 היא 8, ו-8 היא הספרה הקטנה ביותר במספר 8 |
| 37 | 788 | הספרה הגדולה ביותר ב-37 היא 7, והספרה הקטנה ביותר ב-788 היא 7 |
כתבו פונקציה בשם IsBridge בצד שרת, המקבלת שני מספרים שלמים חיוביים ומחזירה אמת אם הם זוג "גשר". אחרת, הפונקציה מחזירה שקר.
הערה: ניתן להשתמש בפעולות עזר.
כתבו פונקציה בשם IsBridge בצד שרת, המקבלת שני מספרים שלמים חיוביים ומחזירה אמת אם הם זוג "גשר". אחרת, הפונקציה מחזירה שקר.
◂ פתרון מלא לשאלה 6
פתרון
return MaxDigit(num1) == MinDigit(num2) || MaxDigit(num2) == MinDigit(num1); (פעולות עזר שמוצאות את הספרה הגדולה/הקטנה ביותר בלולאה עם % 10 ו-/ 10)
מה נדרש: פונקציה בוליאנית שבודקת אם הספרה הגדולה ביותר של אחד המספרים שווה לספרה הקטנה ביותר של המספר האחר.
רעיון:
num % 10 – הספרה הימנית, num / 10 – מחיקתה) ומעדכנת את הגדולה/הקטנה ביותר שנמצאה עד כה.פתרון:
// פעולת עזר: מחזירה את הספרה הגדולה ביותר במספר חיובי
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);
}בדיקה:
| num1 | num2 | גדולה/קטנה ב-num1 | גדולה/קטנה ב-num2 | תוצאה |
|---|---|---|---|---|
| 352 | 579 | 5 / 2 | 9 / 5 | True (5 = 5) |
| 8 | 286 | 8 / 8 | 8 / 2 | True (הגדולה של 286 = 8) |
| 37 | 788 | 7 / 3 | 8 / 7 | True (7 = 7) |
| 90 | 9 | 9 / 0 | 9 / 9 | True |
| 38 | 85 | 8 / 3 | 8 / 5 | False |
| 105 | 1 | 5 / 0 | 1 / 1 | False |
טעות נפוצה: לבדוק כיוון אחד בלבד – אז הזוג (8, 286) מהדוגמה ייכשל. טעות נוספת: לאתחל min = 0 – אז הספרה הקטנה ביותר תצא תמיד 0.
// פעולת עזר: מחזירה את הספרה הגדולה ביותר במספר חיובי
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 (מספר העותקים המושאלים כעת), מסוג מספר שלם.
דוגמה לטבלה ובה כמה מן הספרים בספרייה:
| title | author | year | copies | borrowed |
|---|---|---|---|---|
| מסע אל הכוכבים | דוד לב | 1985 | 3 | 2 |
| גן הסודות | נועה שחר | 1990 | 4 | 0 |
| ימי החורף | רון אלון | 2012 | 5 | 1 |
| הספינה האחרונה | מיכל בר | 1975 | 6 | 3 |
| שירי ערש | מיכל בר | 1948 | 2 | 0 |
| העיר הלבנה | יעל דגן | 2000 | 3 | 1 |
| אור בחלון | רון אלון | 1999 | 2 | 2 |
כתבו שאילתה אחת בשפת SQL המבקשת להציג את שמות הספרים המקיימים אחד או יותר מהתנאים הבאים:
א. בספרייה נותרו פחות משני עותקים פנויים (שאינם מושאלים) של הספר.
ב. הספר יצא לאור לפני שנת 1950 או אחרי שנת 2000.
ג. את הספר כתבה הסופרת נועה שחר.
◂ פתרון מלא לשאלה 7
פתרון
SELECT title FROM BooksTable WHERE copies - borrowed < 2 OR year < 1950 OR year > 2000 OR author = 'נועה שחר';
(על טבלת הדוגמה: מסע אל הכוכבים, גן הסודות, ימי החורף, שירי ערש, אור בחלון)
מה נדרש – שאילתה אחת שמחזירה את שמות הספרים שמקיימים לפחות אחד מהתנאים ⇒ חיבור ב־OR.
רעיון
copies - borrowed. "פחות משניים" ⇒ copies - borrowed < 2.year < 1950 OR year > 2000 (שנת 1950 ושנת 2000 עצמן אינן נכללות). אפשר לכתוב באופן שקול year NOT BETWEEN 1950 AND 2000.author = 'נועה שחר' – טקסט בין גרשיים בודדים.פתרון
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
מועדון טיולים שולח לחבריו עדכונים בדואר אלקטרוני. בטופס ההרשמה לרשימת התפוצה מקלידים כתובת דואר אלקטרוני ושם מלא:
<input type="text" name="email" id="email" />
<input type="text" name="fullName" id="fullName" />בבסיס הנתונים קיימת טבלה בשם subscribers ובה שתי עמודות:
דוגמה לטבלה:
| fullName | |
|---|---|
| dana@mail.com | Dana Shalev |
| yossi@mail.com | Yossi Katz |
כל כתובת דואר אלקטרוני יכולה להופיע בטבלה פעם אחת לכל היותר (שתי כתובות נחשבות זהות רק אם המחרוזות שלהן זהות בדיוק):
לפניכם קטע קוד חלקי תקין של פעולה בשם Subscribe. השלימו את הקוד החסר.
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();
}יש להשלים את הקוד החסר (מסומן בכוכביות).
יש להשלים את הקוד החסר (מסומן בכוכביות).
◂ פתרון מלא לשאלה 8
פתרון
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.
exists = false, ולולאת foreach על כל השורות: אם r["email"].ToString() שווה לכתובת מהטופס ⇐ exists = true. (r["email"] הוא object, לכן ממירים למחרוזת לפני ההשוואה.)exists – Response.Write("כתובת זו כבר רשומה") ולא משנים את הטבלה. אחרת – NewRow(), ממלאים את שתי העמודות, Rows.Add(row) ומציגים "נרשמת בהצלחה".adapter.Update(ds) שבסוף הפעולה שומר במסד רק שורות שנוספו; אם לא הוספנו דבר – אין שינוי במסד. ה-SqlCommandBuilder כבר נמצא בקוד הנתון.פתרון – הקוד החסר (במקום הכוכביות):
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 למחרוזת, שאינה משווה את התוכן.
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 (ותק בשנים, מספר שלם).
דוגמה לטבלה:
| name | department | salary | years |
|---|---|---|---|
| אבי | מכירות | 9000 | 3 |
| בתיה | כספים | 12000 | 12 |
| גיל | פיתוח | 15000 | 4 |
| דנה | מכירות | 9500 | 10 |
| הדר | פיתוח | 14000 | 10 |
| ורד | כספים | 11000 | 9 |
מנהלת משאבי האנוש מעוניינת להפיק טבלה ובה כל עובד המקיים לפחות אחד משני התנאים האלה: העובד שייך למחלקת המכירות, או שהוותק שלו 10 שנים לפחות (בכל מחלקה). עובד המקיים את שני התנאים יופיע בטבלה פעם אחת. הפעולה showEmployees מקבלת מחרוזת התחברות למסד הנתונים connectionString, ומציגה בטבלה את השם, המחלקה והוותק של העובדים המתאימים.
בפעולה חסרים שמונה ביטויים המסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (8), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
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), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
◂ פתרון מלא לשאלה 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> | סגירת הטבלה אחרי הלולאה |
הפעולה המלאה אחרי ההשלמה:
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);
}בדיקה – הפעולה המלאה על נתוני הטבלה מהשאלה. השורות שהוצגו:
| name | department | years |
|---|---|---|
| אבי | מכירות | 3 |
| בתיה | כספים | 12 |
| דנה | מכירות | 10 |
| הדר | פיתוח | 10 |
גיל (פיתוח, 4 שנים) וורד (כספים, 9 שנים) אינם מקיימים אף אחד מהתנאים. (סדר השורות בתוצאה של שאילתה בלי ORDER BY אינו מובטח; מוחזרים בדיוק ארבעת העובדים האלה.)
טעות נפוצה: ב־(3) לכתוב AND – אבל עובד מוצג אם הוא מקיים לפחות אחד מהתנאים ⇒ OR. עם AND הייתה מוצגת רק דנה (עובדת מכירות עם ותק של 10 שנים). דנה מקיימת את שני התנאים, ובכל זאת השאילתה מחזירה אותה פעם אחת בלבד – כל שורה בטבלה נבדקת פעם אחת. ב־(7) לכתוב dr[department] בלי מירכאות – שגיאת הידור, כי אין משתנה בשם department. ב־(8) לשכוח את </table>.
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: |
| 4 | - | (1) "pass" |
| 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# |
| 9 | - | (1) new SqlConnection(connectionString) |