מתכונת 6 במדעי המחשב - תכנות באינטרנט 899372
נבחנים אקסטרניים · שאלון 899372 · 9 שאלות · 2 שעות · פתרון מלא לכל סעיף
מבחן מתכונת במדעי המחשב, תכנות באינטרנט, במבנה של בחינת הבגרות. כל שאלה בו בנויה כמו שאלה מבגרות אחרת. לכל סעיף יש תשובה ופתרון מלא, וכל קוד מופיע ב-Java וב-C#. פתרו בזמן של הבחינה, ורק אחר כך פתחו את הפתרונות.
הוראות הבחינה
א. משך הבחינה: שעתיים.
ב. מבנה השאלון ומפתח הערכה: בשאלון זה שלושה פרקים.
פרק ראשון — (10x3) — 30 נקודות
פרק שני — (20x2)+(10x1) — 50 נקודות
פרק שלישי — (20x1) — 20 נקודות
סך הכול — 100 נקודות
ג. חומר עזר מותר בשימוש: כל חומר עזר, חוץ ממחשבון שיש בו אפשרות תכנות.
יש לכתוב במחברת הבחינה בלבד, יש לרשום "טיוטה" בראש כל עמוד המשמש טיוטה.
כתיבת טיוטה בדפים שאינם במחברת הבחינה עלולה לגרום לפסילת הבחינה.
השאלות בשאלון זה מנוסחות בלשון רבים, אף על פי כן על כל תלמידה וכל תלמיד להשיב עליהן באופן אישי.
בהצלחה!
פרק ראשון
ענו על שלוש השאלות 3-1 (לכל שאלה – 10 נקודות).
שאלה 1 · מהו הלקוח במודל שרת–לקוח
אתרי אינטרנט פועלים במודל שרת–לקוח. מהו הלקוח במודל זה?
העתיקו למחברת הבחינה את התשובה הנכונה מבין האפשרויות א–ד שלפניכם.
א. המחשב שבו שמורים קבצי האתר, ושמריץ את קוד צד השרת.
ב. הדפדפן ששולח בקשה לשרת ומציג למשתמש את הדף שהתקבל.
ג. מסד הנתונים שבו נשמרים נתוני האתר.
ד. השפה שבה כותבים את קוד צד השרת.
העתיקו למחברת הבחינה את התשובה הנכונה מבין האפשרויות א–ד.
◂ פתרון מלא לשאלה 1
פתרון
ב. הדפדפן ששולח בקשה לשרת ומציג למשתמש את הדף שהתקבל.
מה נדרש: לבחור את התיאור הנכון של ה"לקוח" במודל שרת–לקוח.
רעיון: במודל שרת–לקוח יש שני צדדים:
פתרון – מעבר על האפשרויות:
תשובה: ב.
בדיקה: סדר הפעולות כשגולשים לדף: המשתמש מקליד כתובת ← הדפדפן (הלקוח) שולח בקשה ← השרת מריץ קוד ובונה דף ← השרת שולח את הדף ← הדפדפן מציג אותו. רק אפשרות ב' מתארת את מי ששולח את הבקשה ומציג את התוצאה.
טעות נפוצה: לחשוב שהלקוח הוא "האדם" או "מי שמשלם" – במודל שרת–לקוח הלקוח הוא התוכנה (הדפדפן) שפונה לשרת.
שאלה 2 · שאילתת SQL – חוגים במתנ"ס
במסד הנתונים של מתנ"ס מוגדרת טבלה בשם Courses ובה נתוני החוגים של השנה. בטבלה יש שש עמודות:
א. שם החוג (courseName).
ב. שם המדריך (instructor).
ג. יום בשבוע (day).
ד. מחיר לחודש (price).
ה. מספר המקומות המרבי בחוג (maxPlaces).
ו. מספר הנרשמים לחוג (registered).
כתבו שאילתה בשפת SQL המאחזרת את שמות החוגים שבהם מספר הנרשמים קטן ממספר המקומות המרבי (כלומר, נשארו בהם מקומות פנויים).
◂ פתרון מלא לשאלה 2
פתרון
SELECT courseName FROM Courses WHERE registered < maxPlaces;
מה נדרש – שאילתה שמחזירה רק את עמודת שם החוג, עבור השורות שבהן registered < maxPlaces.
רעיון – SELECT של העמודה courseName, FROM הטבלה Courses, ותנאי WHERE שמשווה בין שתי עמודות באותה שורה: מספר הנרשמים לעומת מספר המקומות המרבי.
פתרון
SELECT courseName
FROM Courses
WHERE registered < maxPlaces;בדיקה – השאילתה על טבלה קטנה לדוגמה:
| courseName | instructor | day | price | maxPlaces | registered |
|---|---|---|---|---|---|
| קרמיקה | מירב | ראשון | 180 | 12 | 12 |
| שחמט | אלכס | שני | 120 | 20 | 14 |
| ג׳ודו | רון | שלישי | 150 | 15 | 16 |
| גיטרה | שירה | רביעי | 220 | 8 | 3 |
התוצאה: שחמט (14 < 20) ו־גיטרה (3 < 8). קרמיקה לא הוחזרה כי היא מלאה (12 = 12 – אין מקום פנוי), וג׳ודו לא הוחזר כי נרשמו אליו יותר מהמקומות.
טעות נפוצה: לכתוב <= – אז גם חוג מלא (מספר הנרשמים שווה למספר המקומות) יוחזר, למרות שאין בו מקום פנוי. אפשר לכתוב את התנאי גם בצורה maxPlaces > registered או maxPlaces - registered > 0.
שאלה 3 · שאילתת SELECT עם תנאי כפול
במסד נתונים של גשרים מוגדרת טבלה בשם Bridges ובה פרטי הגשרים. בטבלה יש ארבע עמודות:
א. שם הגשר (name).
ב. אורך הגשר במטרים (length).
ג. השנה שבה הסתיימה בניית הגשר (yearBuilt).
ד. העיר שבה נמצא הגשר (city).
כתבו שאילתה בשפת SQL המאחזרת את שמות הגשרים ואת העיר שבה נמצא כל גשר, בגשרים שאורכם גדול מ-1,000 מטר ושבנייתם הסתיימה בשנת 1950 או לפני כן.
כתבו שאילתה בשפת SQL המאחזרת את שמות הגשרים ואת העיר שבה נמצא כל גשר, בגשרים שאורכם גדול מ-1,000 מטר ושבנייתם הסתיימה בשנת 1950 או לפני כן.
◂ פתרון מלא לשאלה 3
פתרון
SELECT name, city FROM Bridges WHERE length > 1000 AND yearBuilt <= 1950
מה נדרש: שאילתה שמחזירה שתי עמודות (name, city) רק עבור גשרים שמקיימים שני תנאים.
רעיון:
SELECT name, city – רק העמודות המבוקשות (לא *).FROM Bridges – שם הטבלה.WHERE עם שני תנאים מחוברים ב-AND (שניהם חייבים להתקיים):length > 1000 (גדול ממש – גשר באורך 1000 בדיוק לא נכלל; המספר נכתב בלי פסיק);yearBuilt <= 1950 ("או לפני כן" – גם 1950 עצמה נכללת).פתרון:
SELECT name, city
FROM Bridges
WHERE length > 1000 AND yearBuilt <= 1950בדיקה: השאילתה על טבלה לדוגמה (בשאלה אין שורות) עם חמישה גשרים ועוד שלוש שורות גבול. התקבלו: Golden Gate (2737 מ', 1937) – San Francisco, Brooklyn Bridge (1834 מ', 1883) – New York, ושורת הגבול שנבנתה בשנת 1950 בדיוק. לא נבחרו: Tower Bridge (244 מ' – קצר מדי), Oresund (2000 – מאוחר מדי), שורת גבול משנת 1951 ושורת גבול שאורכה 1000 בדיוק.
טעות נפוצה: לכתוב yearBuilt < 1950 (ואז גשר משנת 1950 לא ייבחר), או לחבר את התנאים ב-OR במקום AND.
פרק שני
ענו על שלוש שאלות: על שאלה 4 (חובה – 10 נקודות) ועל שתיים מן השאלות 7-5 (לכל שאלה – 20 נקודות).
שאלה 4 · JavaScript – בדיקת קוד כפול בשידור (השלמת קוד)
באתר לניהול רחפני משלוחים יש שדה שבו המפעיל מזין את קוד האימות של הרחפן:
<input type="text" maxlength="6" name="droneCode" id="droneCode" />כדי לזהות שיבושים בשידור, הקוד המקורי (original) נשלח לרחפן כמחרוזת (sent) שבה כל תו של הקוד מופיע פעמיים ברצף. כלומר, התו הראשון של original מופיע במקומות 0 ו־1 של sent, התו השני במקומות 2 ו־3, וכן הלאה. לכן אורכה של sent הוא בדיוק פי 2 מאורכה של original.
דוגמה:
original = "HI5"
sent = "HHII55"
השידור תקין.
דוגמאות נוספות עבור original = "HI5":
sent = "HHIJ55" – השידור שגוי (התו במקום 3 אינו I)
sent = "HIHI55" – השידור שגוי (התווים אינם מופיעים בזוגות)
sent = "HHII555" – השידור שגוי (אורך שגוי)
הפעולה isDoubled שלפניכם מחזירה true אם sent הוא שידור תקין של original, ו־false אחרת. בפעולה חסרים שישה ביטויים המסומנים במספרים (1)–(6).
כתבו במחברת הבחינה את המספרים (1)–(6) ואת הביטויים החסרים שכל מספר מייצג.
<script>
function isDoubled (original, sent)
{
// במחרוזת שנשלחה חייבים להיות בדיוק פי 2 תווים
if (_______(1)_______)
return false;
var j = 0;
for (var i = 0; _______(2)_______; i++)
{
var c = _______(3)_______;
// שני התווים בזוג הנוכחי של sent חייבים להיות שווים ל־c
if (_______(4)_______ || _______(5)_______) // (4) – התו הראשון בזוג, (5) – התו השני בזוג
return false;
// נעבור לזוג הבא
_____(6)_____; // j
}
return true;
}
</script>כתבו במחברת הבחינה את המספרים (1)–(6) ואת הביטויים החסרים שכל מספר מייצג.
◂ פתרון מלא לשאלה 4
פתרון
(1) sent.length != 2 * original.length
(2) i < original.length
(3) original.charAt(i)
(4) sent.charAt(j) != c
(5) sent.charAt(j + 1) != c
(6) j = j + 2
מה נדרש – להשלים שישה ביטויים בפעולה שבודקת אם sent היא original שבה כל תו הוכפל.
רעיון
i עובר על original תו אחרי תו, ו-j עובר על sent בקפיצות של 2 – j הוא תמיד תחילת הזוג שמתאים לתו ה-i (j = 2·i).פתרון
| מספר | ביטוי | הסבר |
|---|---|---|
| (1) | sent.length != 2 * original.length | האורך של sent אינו פי 2 מהאורך של original |
| (2) | i < original.length | עוברים על כל תווי original |
| (3) | original.charAt(i) | התו הנוכחי של הקוד המקורי |
| (4) | sent.charAt(j) != c | התו הראשון בזוג שונה מ-c |
| (5) | sent.charAt(j + 1) != c | התו השני בזוג שונה מ-c |
| (6) | j = j + 2 | מעבר לזוג הבא ב-sent |
גם ביטויים שקולים מתקבלים, למשל sent.length != original.length * 2 ב-(1) ו-j += 2 ב-(6).
הפעולה המלאה:
function isDoubled (original, sent)
{
// במחרוזת שנשלחה חייבים להיות בדיוק פי 2 תווים
if (sent.length != 2 * original.length)
return false;
var j = 0;
for (var i = 0; i < original.length; i++)
{
var c = original.charAt(i);
// שני התווים בזוג הנוכחי של sent חייבים להיות שווים ל־c
if (sent.charAt(j) != c || sent.charAt(j + 1) != c) // (4) – התו הראשון בזוג, (5) – התו השני בזוג
return false;
// נעבור לזוג הבא
j = j + 2; // j
}
return true;
}בדיקה
מעקב עבור original = "HI5", sent = "HHII55" (אורכים 3 ו־6, ולכן (1) שקר):
| i | j | c | sent.charAt(j) | sent.charAt(j + 1) | תקין? |
|---|---|---|---|---|---|
| 0 | 0 | H | H | H | כן |
| 1 | 2 | I | I | I | כן |
| 2 | 4 | 5 | 5 | 5 | כן |
כש-i = 3 התנאי (2) שקר, הלולאה מסתיימת ומוחזר true.
התוצאות:
isDoubled("HI5", "HHII55") → trueisDoubled("HI5", "HHIJ55") → false (ב-i = 1: התו השני בזוג הוא J ולא I)isDoubled("HI5", "HIHI55") → false (ב-i = 0: התו השני בזוג הוא I ולא H)isDoubled("HI5", "HHII5") ו-isDoubled("HI5", "HHII555") → false (נפסלים כבר בבדיקת האורך (1))טעות נפוצה: לוותר על בדיקת האורך (1) – בלעדיה sent עם תווים מיותרים בסוף (למשל "HHII555") הייתה מתקבלת כתקינה, כי הלולאה עוברת רק על תווי original. טעות נוספת: לקדם את j ב־1 במקום ב־2.
שאלה 5 · הצגת ימים גשומים מבסיס הנתונים – השלמת ביטויים
תחנה מטאורולוגית שומרת את מדידותיה היומיות במסד נתונים, בטבלה - Measurements. בכל שורה בטבלה: התאריך (Day, מחרוזת בתבנית שנה-חודש-יום, למשל 2027-01-02), הטמפרטורה (Temp) וכמות הגשם שירדה באותו יום במילימטרים (Rain, מספר שלם; 0 – לא ירד גשם).
לפניכם פונקציה שכותרתה: protected void printRainyDays(string connectionString)
הפונקציה מקבלת מחרוזת connectionString שמפנה לבסיס הנתונים, ומציגה בדף טבלה של הימים הגשומים בלבד (ימים שבהם ירד גשם), מסודרים לפי התאריך, ומתחת לטבלה את מספר הימים הגשומים.
דוגמה לפלט של הפונקציה עם הנתונים הקיימים:
ימים גשומים
| תאריך | כמות גשם (מ"מ) |
|---|---|
| 2027-01-02 | 12 |
| 2027-01-03 | 31 |
| 2027-01-05 | 4 |
| 2027-01-07 | 22 |
מספר הימים הגשומים: 4
בפונקציה חסרים שמונה ביטויים מסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (8), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
protected void printRainyDays(string connectionString)
{
SqlConnection connection = new SqlConnection(connectionString);
string sql = "SELECT Day, Rain FROM Measurements ______(1)______ ORDER BY Day";
SqlCommand cmd = new SqlCommand(sql, ______(2)______);
SqlDataAdapter adapter = ______(3)______;
DataTable dt = new DataTable();
connection.Open();
adapter.______(4)______;
connection.______(5)______;
Response.Write("<h2>ימים גשומים</h2>");
Response.Write("<table border='1'><tr><th>תאריך</th><th>כמות גשם (מ\"מ)</th></tr>");
foreach (DataRow r in ______(6)______)
{
Response.Write("<tr><td>" + r["Day"] + "</td><td>" + ______(7)______ + "</td></tr>");
}
Response.Write("</table>");
Response.Write("מספר הימים הגשומים: " + ______(8)______);
}בפונקציה חסרים שמונה ביטויים מסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (8), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
◂ פתרון מלא לשאלה 5
פתרון
(1) WHERE Rain > 0
(2) connection
(3) new SqlDataAdapter(cmd)
(4) Fill(dt)
(5) Close()
(6) dt.Rows
(7) r["Rain"]
(8) dt.Rows.Count
מה נדרש – להשלים 8 ביטויים בפונקציה שקוראת ממסד הנתונים את הימים הגשומים, מציגה אותם בטבלת HTML ומדפיסה את מספרם.
רעיון – הסינון נעשה בשאילתה (WHERE), כך שהטבלה שחוזרת מכילה רק ימים גשומים, ומספר השורות שלה הוא בדיוק מספר הימים הגשומים. סדר הפעולות: חיבור ← פקודה עם שאילתה וחיבור ← מתאם ← פתיחת חיבור, Fill לתוך DataTable, סגירה ← לולאה על השורות עם Response.Write.
פתרון
| מספר | הביטוי החסר | הסבר |
|---|---|---|
| (1) | WHERE Rain > 0 | תנאי הסינון: רק ימים שבהם כמות הגשם גדולה מ-0. (גם WHERE Rain <> 0 נכון, כי כמות גשם אינה שלילית.) |
| (2) | connection | הבנאי של SqlCommand מקבל את השאילתה ואת החיבור – connection. |
| (3) | new SqlDataAdapter(cmd) | יוצרים מתאם על הפקודה cmd. |
| (4) | Fill(dt) | המתאם מריץ את השאילתה וממלא את ה-DataTable בשם dt: Fill(dt). |
| (5) | Close() | סוגרים את החיבור אחרי שהנתונים נטענו. |
| (6) | dt.Rows | עוברים על אוסף השורות של הטבלה – dt.Rows. |
| (7) | r["Rain"] | בתא השני מציגים את כמות הגשם של השורה – r["Rain"]. |
| (8) | dt.Rows.Count | מספר הימים הגשומים הוא מספר השורות שהשאילתה החזירה – dt.Rows.Count. |
הפונקציה המלאה:
protected void printRainyDays(string connectionString)
{
SqlConnection connection = new SqlConnection(connectionString);
string sql = "SELECT Day, Rain FROM Measurements WHERE Rain > 0 ORDER BY Day";
SqlCommand cmd = new SqlCommand(sql, connection);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
connection.Open();
adapter.Fill(dt);
connection.Close();
Response.Write("<h2>ימים גשומים</h2>");
Response.Write("<table border='1'><tr><th>תאריך</th><th>כמות גשם (מ\"מ)</th></tr>");
foreach (DataRow r in dt.Rows)
{
Response.Write("<tr><td>" + r["Day"] + "</td><td>" + r["Rain"] + "</td></tr>");
}
Response.Write("</table>");
Response.Write("מספר הימים הגשומים: " + dt.Rows.Count);
}בדיקה – טבלה עם 7 ימי מדידה (3 מהם בלי גשם) והפונקציה המלאה ב-C#. נשלחה השאילתה SELECT Day, Rain FROM Measurements WHERE Rain > 0 ORDER BY Day והפלט היה בדיוק כמו בדוגמה: ארבע שורות (2027-01-02 – 12, 2027-01-03 – 31, 2027-01-05 – 4, 2027-01-07 – 22) ואחריהן "מספר הימים הגשומים: 4".
טעות נפוצה: לכתוב ב-(8) dt.Rows (אוסף, לא מספר) או לספור את כל הימים בטבלה במקום את הימים שהשאילתה החזירה; וב-(6) לכתוב dt במקום dt.Rows.
protected void printRainyDays(string connectionString)
{
SqlConnection connection = new SqlConnection(connectionString);
string sql = "SELECT Day, Rain FROM Measurements WHERE Rain > 0 ORDER BY Day";
SqlCommand cmd = new SqlCommand(sql, connection);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
connection.Open();
adapter.Fill(dt);
connection.Close();
Response.Write("<h2>ימים גשומים</h2>");
Response.Write("<table border='1'><tr><th>תאריך</th><th>כמות גשם (מ\"מ)</th></tr>");
foreach (DataRow r in dt.Rows)
{
Response.Write("<tr><td>" + r["Day"] + "</td><td>" + r["Rain"] + "</td></tr>");
}
Response.Write("</table>");
Response.Write("מספר הימים הגשומים: " + dt.Rows.Count);
}שאלה 6 · שיבוץ שחקן לטורניר שחמט לפי Session
באתר של מועדון שחמט הוגדרו שני משתני Session:
כתבו פונקציה בצד השרת שכותרתה protected void enterTournament()
הפונקציה מחליטה לאיזו קבוצה בטורניר נכנס המשתמש ומפנה לדף המתאים:
כתבו פונקציה בצד השרת שכותרתה protected void enterTournament() שמפנה את המשתמש לדף המתאים ושומרת את הקבוצה שלו ב-Session.
◂ פתרון מלא לשאלה 6
פתרון
if (Session["userName"] == null) Response.Redirect("login.aspx"); else { int rating = (int)Session["rating"]; if (rating >= 1800) { Session["group"] = "masters"; Response.Redirect("masters.aspx"); } else { Session["group"] = "open"; Response.Redirect("open.aspx"); } }
מה נדרש – פעולה בצד השרת שקוראת שני משתני Session, שומרת משתנה Session חדש ומפנה (Response.Redirect) לאחד משלושה דפים.
רעיון – קודם בודקים אם המשתמש מחובר: אם Session["userName"] הוא null – login.aspx. אחרת קוראים את הדירוג – ב-Session הערכים נשמרים מטיפוס object, ולכן צריך המרה (int). לפי הדירוג שומרים את שם הקבוצה ב-Session["group"] ורק אחר כך מפנים, כי Response.Redirect מסיים את הטיפול בבקשה – שורה שנכתבת אחריו לא תתבצע.
פתרון
protected void enterTournament()
{
if (Session["userName"] == null)
{
Response.Redirect("login.aspx");
}
else
{
int rating = (int)Session["rating"];
if (rating >= 1800)
{
Session["group"] = "masters";
Response.Redirect("masters.aspx");
}
else
{
Session["group"] = "open";
Response.Redirect("open.aspx");
}
}
}בדיקה:
| userName | rating | הפניה | Session["group"] |
|---|---|---|---|
| לא הוגדר | לא הוגדר | login.aspx | לא הוגדר |
| לא הוגדר | 2000 | login.aspx | לא הוגדר |
| dana | 2150 | masters.aspx | masters |
| avi | 1800 | masters.aspx | masters |
| tom | 1799 | open.aspx | open |
| lior | 950 | open.aspx | open |
טעות נפוצה: לכתוב if (Session["rating"] >= 1800) בלי המרה – שגיאת הידור, כי Session מחזיר object; לכתוב rating > 1800 (שחקן עם דירוג 1800 בדיוק צריך להגיע ל-masters); ולשמור את Session["group"] אחרי Response.Redirect – השמירה לא תתבצע.
protected void enterTournament()
{
if (Session["userName"] == null)
{
Response.Redirect("login.aspx");
}
else
{
int rating = (int)Session["rating"];
if (rating >= 1800)
{
Session["group"] = "masters";
Response.Redirect("masters.aspx");
}
else
{
Session["group"] = "open";
Response.Redirect("open.aspx");
}
}
}שאלה 7 · SQL – טבלת WeatherLog (3 מתוך 6 סעיפים)
במסד נתונים של שירות מזג אוויר יש טבלה ששמה WeatherLog המכילה מדידות יומיות בערים שונות. העמודות בטבלה הן:
דוגמה לטבלה ובה מדידות:
| dayId | city | minTemp | maxTemp | rainMm | windKmh |
|---|---|---|---|---|---|
| 1 | Haifa | 18 | 27 | 0 | 25 |
| 2 | Eilat | 24 | 38 | 0 | 45 |
| 3 | Safed | 9 | 16 | 12 | 30 |
| 4 | Tel Aviv | 19 | 25 | 4 | 8 |
| 5 | Safed | 7 | 13 | 20 | 50 |
ענו על 3 מתוך 6 הסעיפים שלפניכם:
כתבו שאילתה בשפת SQL המחזירה את כמות המדידות שבהן לא ירד גשם (rainMm שווה 0) וגם הטמפרטורה הגבוהה ביותר (maxTemp) הייתה 25 מעלות או יותר.
כתבו שאילתה בשפת SQL המחזירה את הטמפרטורה הנמוכה ביותר שנמדדה (minTemp) בכל המדידות.
כתבו שאילתה בשפת SQL המחזירה את שמות הערים ומזהי המדידות שבהן ההפרש בין הטמפרטורה הגבוהה ביותר לטמפרטורה הנמוכה ביותר גדול מ־10 מעלות.
כתבו שאילתה בשפת SQL המחזירה את ממוצע כמות הגשם (rainMm) בכל המדידות.
כתבו שאילתה בשפת SQL המחזירה את מזהי המדידות שבהן מהירות הרוח קטנה מ־10 קמ"ש או גדולה מ־40 קמ"ש.
כתבו שאילתה בשפת SQL המחזירה את שמות הערים (ללא כפילויות) שבשמן מופיעה האות f.
◂ פתרון מלא לשאלה 7
סעיף א
SELECT COUNT(*) FROM WeatherLog WHERE rainMm = 0 AND maxTemp >= 25; (בטבלת הדוגמה: 2)
מה נדרש – מספר (כמות) המדידות, לא המדידות עצמן.
רעיון – COUNT(*) סופרת את השורות שעברו את הסינון. שני התנאים חייבים להתקיים יחד ⇐ AND. "25 או יותר" ⇐ >= 25.
פתרון
SELECT COUNT(*)
FROM WeatherLog
WHERE rainMm = 0 AND maxTemp >= 25;בדיקה – בטבלת הדוגמה: Haifa (0 מ"מ, 27) – כן; Eilat (0, 38) – כן; Safed (12 מ"מ) – לא; Tel Aviv (4 מ"מ) – לא; Safed (20 מ"מ) – לא. התוצאה: 2. בטבלה מורחבת שהוספנו לה מדידה בלי גשם עם maxTemp = 25 בדיוק, היא נספרה (3).
טעות נפוצה: לכתוב maxTemp > 25 – אז מדידה של 25 בדיוק לא תיספר.
סעיף ב
SELECT MIN(minTemp) FROM WeatherLog; (בטבלת הדוגמה: 7)
מה נדרש – ערך אחד – המינימום של העמודה minTemp.
רעיון – הפונקציה המצרפת MIN מחזירה את הערך הקטן ביותר בעמודה, בלי צורך ב-WHERE.
פתרון
SELECT MIN(minTemp)
FROM WeatherLog;בדיקה – הערכים בעמודה הם 18, 24, 9, 19, 7 ⇐ השאילתה מחזירה 7.
טעות נפוצה: לכתוב SELECT minTemp FROM WeatherLog WHERE minTemp = MIN(minTemp) – אי אפשר להשתמש בפונקציה מצרפת בתוך WHERE.
סעיף ג
SELECT city, dayId FROM WeatherLog WHERE maxTemp - minTemp > 10; (בטבלת הדוגמה: Eilat | 2)
מה נדרש – שתי עמודות (city, dayId) של השורות המתאימות.
רעיון – ב-WHERE אפשר לחשב ביטוי מתוך שתי עמודות של אותה שורה: maxTemp - minTemp, ולהשוות אותו ל־10. "גדול מ־10" ⇐ > 10.
פתרון
SELECT city, dayId
FROM WeatherLog
WHERE maxTemp - minTemp > 10;בדיקה – ההפרשים בטבלת הדוגמה: 9, 14, 7, 6, 6 ⇐ רק Eilat (מדידה 2). השאילתה מחזירה Eilat | 2. בטבלה מורחבת עם מדידות שבהן ההפרש הוא 10 בדיוק – הן לא הוחזרו, כנדרש.
טעות נפוצה: לכתוב minTemp - maxTemp > 10 (ההפרש יוצא שלילי ושום שורה לא תוחזר).
סעיף ד
SELECT AVG(rainMm) FROM WeatherLog; (בטבלת הדוגמה: 7.2; ב-SQL Server, שבו AVG על עמודה מטיפוס מספר שלם מחזיר מספר שלם: 7)
מה נדרש – ערך אחד – הממוצע של העמודה rainMm.
רעיון – הפונקציה המצרפת AVG מחשבת סכום חלקי מספר השורות. הערה: ב-SQL Server, כשהעמודה מטיפוס מספר שלם, גם הממוצע מחושב כמספר שלם (7); כדי לקבל ממוצע עם שבר כותבים AVG(rainMm * 1.0). ב-Access מתקבל 7.2.
פתרון
SELECT AVG(rainMm)
FROM WeatherLog;בדיקה – (0 + 0 + 12 + 4 + 20) / 5 = 36 / 5 = 7.2.
טעות נפוצה: לכתוב SUM(rainMm) (מחזיר 36, הסכום ולא הממוצע).
סעיף ה
SELECT dayId FROM WeatherLog WHERE windKmh < 10 OR windKmh > 40; (בטבלת הדוגמה: 2, 4, 5)
מה נדרש – העמודה dayId של המדידות שבהן הרוח מחוץ לטווח 10–40.
רעיון – מספר לא יכול להיות גם קטן מ־10 וגם גדול מ־40, ולכן מחברים את שני התנאים ב-OR.
פתרון
SELECT dayId
FROM WeatherLog
WHERE windKmh < 10 OR windKmh > 40;בדיקה – מהירויות הרוח: 25, 45, 30, 8, 50 ⇐ מתאימות 45 (מדידה 2), 8 (מדידה 4), 50 (מדידה 5). השאילתה מחזירה 2, 4, 5.
טעות נפוצה: לחבר ב-AND – אז לא תוחזר אף שורה.
סעיף ו
SELECT DISTINCT city FROM WeatherLog WHERE city LIKE '%f%'; (בטבלת הדוגמה: Haifa, Safed)
מה נדרש – שמות ערים, כל שם פעם אחת בלבד.
רעיון – LIKE '%f%' – התו % מחליף רצף כלשהו של תווים (גם ריק), כך שהאות f יכולה להופיע בכל מקום בשם. DISTINCT מסיר כפילויות (Safed מופיעה פעמיים בטבלה). ב-Access תו הכללי הוא *: LIKE '*f*'.
פתרון
SELECT DISTINCT city
FROM WeatherLog
WHERE city LIKE '%f%';בדיקה – Haifa – כן; Eilat – לא; Safed (פעמיים) – כן; Tel Aviv – לא. השאילתה מחזירה Haifa, Safed (בלי DISTINCT היינו מקבלים את Safed פעמיים).
טעות נפוצה: לכתוב LIKE 'f' בלי תווי % – זה מחפש שם שהוא בדיוק "f".
פרק שלישי
ענו על שאלה אחת מן השאלות 9-8.
שאלה 8 · מכירת כרטיסים מוגבלת עם Session ו-Application (20 נקודות)
לקראת משחק הגמר של נבחרת הכדורסל של בית הספר נמכרים באתר בית הספר 100 כרטיסים בלבד. כל תלמיד רשאי לקנות עד 2 כרטיסים.
בכל טעינה של הדף (לחיצה על הכפתור "קנה כרטיס") הקוד שלפניכם מנסה להוסיף כרטיס אחד להזמנה של התלמיד.
protected void Page_Load (object sender, EventArgs e)
{
if (Session["bought"] == null) // איתחול
Session["bought"] = 0;
if ((int)Session["bought"] == ______(1)______) // כל תלמיד רשאי לקנות עד 2 כרטיסים
Response.Write("הגעת למספר הכרטיסים המרבי");
else
{
Application.______(2)______();
if (Application["left"] == ______(3)______) // איתחול המלאי
Application["left"] = 100;
if (______(4)______ > 0)
{
Application["left"] = (int)Application["left"] - 1;
Session["bought"] = ______(5)______;
Response.Write("הכרטיס נוסף להזמנה שלך");
}
else
Response.Write("הכרטיסים אזלו");
Application.UnLock();
}
}השלימו את הקוד: כיתבו ליד כל מספר 5-1, את הביטוי החסר.
הסבירו מה התפקיד של Application["left"] בקטע הקוד.
הסבירו מה התפקיד של Session["bought"] בקטע הקוד.
◂ פתרון מלא לשאלה 8
סעיף א
(1) 2 (2) Lock (3) null (4) (int)Application["left"] (5) (int)Session["bought"] + 1
מה נדרש: להשלים חמישה ביטויים בקוד שמוכר כרטיסים ממלאי משותף ומגביל כל תלמיד ל-2 כרטיסים.
רעיון:
Session["bought"] – מונה אישי של הכרטיסים שהתלמיד הנוכחי כבר קנה. כשהוא מגיע ל-2 – לא מוכרים לו עוד.Application["left"] – מונה משותף לכל האתר של הכרטיסים שנשארו. בפעם הראשונה הוא null ולכן מאותחל ל-100.פתרון:
| מספר | הביטוי החסר | הסבר |
|---|---|---|
| (1) | 2 | התלמיד כבר קנה 2 כרטיסים – המספר המרבי |
| (2) | Lock | Application.Lock(); – נעילה לפני קריאה ועדכון של המלאי המשותף |
| (3) | null | שורת איתחול (כמו Session["bought"] בתחילת הפעולה): בטעינה הראשונה באתר המשתנה Application["left"] עדיין לא קיים וערכו null – מאתחלים ל-100 |
| (4) | (int)Application["left"] | יש עוד כרטיסים במלאי (המרה ל-int לפני ההשוואה) |
| (5) | (int)Session["bought"] + 1 | הכרטיס נוסף להזמנה – מונה התלמיד גדל ב-1 |
הקוד המלא:
protected void Page_Load (object sender, EventArgs e)
{
if (Session["bought"] == null) // איתחול
Session["bought"] = 0;
if ((int)Session["bought"] == 2) // כל תלמיד רשאי לקנות עד 2 כרטיסים
Response.Write("הגעת למספר הכרטיסים המרבי");
else
{
Application.Lock();
if (Application["left"] == null) // איתחול המלאי
Application["left"] = 100;
if ((int)Application["left"] > 0)
{
Application["left"] = (int)Application["left"] - 1;
Session["bought"] = (int)Session["bought"] + 1;
Response.Write("הכרטיס נוסף להזמנה שלך");
}
else
Response.Write("הכרטיסים אזלו");
Application.UnLock();
}
}בדיקה: Session נפרד לכל תלמיד ו-Application משותף:
| פעולה | bought של התלמיד | left | פלט |
|---|---|---|---|
| תלמיד א – קנייה 1 | 1 | 99 | הכרטיס נוסף להזמנה שלך |
| תלמיד א – קנייה 2 | 2 | 98 | הכרטיס נוסף להזמנה שלך |
| תלמיד א – קנייה 3 | 2 | 98 | הגעת למספר הכרטיסים המרבי |
| תלמיד ב – קנייה 1 | 1 | 97 | הכרטיס נוסף להזמנה שלך |
| עוד 48 תלמידים קונים 2 כרטיסים כל אחד, ותלמיד ב קונה עוד אחד | – | 0 | – |
| תלמיד חדש – קנייה 1 | 0 | 0 | הכרטיסים אזלו |
טעות נפוצה: לכתוב ב-(1) 3 – אז תלמיד יוכל לקנות 3 כרטיסים. טעות נוספת: לכתוב ב-(4) Application["left"] > 0 בלי המרה – לא עובר הידור.
protected void Page_Load (object sender, EventArgs e)
{
if (Session["bought"] == null) // איתחול
Session["bought"] = 0;
if ((int)Session["bought"] == 2) // כל תלמיד רשאי לקנות עד 2 כרטיסים
Response.Write("הגעת למספר הכרטיסים המרבי");
else
{
Application.Lock();
if (Application["left"] == null) // איתחול המלאי
Application["left"] = 100;
if ((int)Application["left"] > 0)
{
Application["left"] = (int)Application["left"] - 1;
Session["bought"] = (int)Session["bought"] + 1;
Response.Write("הכרטיס נוסף להזמנה שלך");
}
else
Response.Write("הכרטיסים אזלו");
Application.UnLock();
}
}סעיף ב
מונה משותף לכל משתמשי האתר של מספר הכרטיסים שנשארו למכירה (מתחיל ב-100 ויורד ב-1 בכל קנייה של תלמיד כלשהו).
מה נדרש: להסביר מה מייצג המשתנה Application["left"].
רעיון: משתנה Application נשמר בשרת אחד לכל האתר ומשותף לכל המשתמשים.
פתרון: Application["left"] הוא מלאי הכרטיסים שנשארו למכירה – אחד לכל האתר. בטעינה הראשונה באתר (null) הוא מאותחל ל-100; בכל קנייה מוצלחת של תלמיד כלשהו הוא קטן ב-1, וכשהוא 0 מוצגת לכל התלמידים ההודעה "הכרטיסים אזלו". מאחר שתלמידים רבים קונים במקביל, הבדיקה והעדכון שלו נעשים בין Application.Lock() ל-Application.UnLock(), כדי שלא יימכרו יותר מ-100 כרטיסים.
בדיקה: אחרי שתלמיד א קנה 2 ותלמיד ב קנה 1, הערך הוא 97 – והתלמיד הבא רואה את אותו ערך.
סעיף ג
מונה אישי לכל תלמיד של הכרטיסים שכבר קנה; כשהוא מגיע ל-2 התלמיד לא יכול לקנות עוד.
מה נדרש: להסביר מה מייצג המשתנה Session["bought"].
רעיון: משתנה Session נשמר בשרת בנפרד לכל משתמש, ולכן מתאים למידע אישי שנשמר בין טעינות של הדף.
פתרון: Session["bought"] הוא מספר הכרטיסים שהתלמיד הנוכחי כבר קנה. בטעינה הראשונה שלו הוא מאותחל ל-0; בכל קנייה מוצלחת הוא גדל ב-1; לפני כל ניסיון קנייה בודקים אותו, וכשהגיע ל-2 מוצגת ההודעה "הגעת למספר הכרטיסים המרבי" ולא מוכרים לו עוד. כך כל תלמיד מוגבל ל-2 כרטיסים בלי להשפיע על תלמידים אחרים.
בדיקה: תלמיד א נעצר אחרי 2 כרטיסים (bought = 2), ובאותו זמן תלמיד ב (bought = 1) ממשיך לקנות.
שאלה 9 · הצגת טבלה ממסד נתונים ב-ASP.NET – נסיעות רכבת
באתר של חברת רכבות נשמרת במסד הנתונים טבלה ששמה Trips ובה נתוני הנסיעות של היום. העמודות בטבלה הן:
דוגמה לטבלה:
| tripNum | fromStation | toStation | departure | freeSeats |
|---|---|---|---|---|
| 311 | "עכו" | "נהריה" | "07:15" | 40 |
| 312 | "עכו" | "בנימינה" | "08:05" | 0 |
| 520 | "דימונה" | "באר שבע" | "06:40" | 12 |
| 315 | "עכו" | "בית שאן" | "09:30" | 3 |
| 521 | "דימונה" | "באר שבע" | "07:40" | 0 |
| 740 | "מודיעין" | "ירושלים" | "08:20" | 0 |
הנוסע בוחר באתר תחנת מוצא. הפונקציה ShowTrips מקבלת מחרוזת התחברות למסד הנתונים connectionString ואת שם התחנה שנבחרה station, ומציגה בטבלה את מספר הנסיעה, היעד ושעת היציאה של כל הנסיעות מהתחנה שנבחרה שיש בהן מקומות פנויים. אם אין אף נסיעה כזו, מוצגת ההודעה "אין נסיעות עם מקומות פנויים מתחנה זו".
בפונקציה חסרים שמונה ביטויים מסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (8-1), וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
protected void ShowTrips(string connectionString, string station)
{
string res = "";
DataSet ds = new DataSet();
SqlConnection conn = new SqlConnection(______(1)______);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT tripNum, toStation, departure FROM [dbo].[Trips] WHERE fromStation = '" + ___(2)___ + "' AND freeSeats ___(3)___ 0";
cmd.Connection = conn;
conn.Open();
SqlDataAdapter adapter = new SqlDataAdapter(______(4)______);
adapter.Fill(ds);
conn.Close();
if (ds.Tables[0].Rows.______(5)______ == 0)
res = "אין נסיעות עם מקומות פנויים מתחנה זו";
else
{
res += "<table><tr><th>מספר נסיעה</th><th>יעד</th><th>שעת יציאה</th></tr>";
foreach (DataRow dr in ds.Tables[0].______(6)______)
{
res += "<tr><td>" + dr["tripNum"] + "</td><td>" + dr["toStation"] + "</td>";
res += "<___(7)___>" + dr["departure"] + "</td></tr>";
}
res += "</table>";
}
Response.______(8)______(res);
}כתבו במחברת הבחינה את מספרי הביטויים החסרים (8-1), וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
◂ פתרון מלא לשאלה 9
פתרון
(1) connectionString (2) station (3) > (4) cmd (5) Count (6) Rows (7) td (8) Write
מה נדרש: להשלים שמונה ביטויים בפונקציה ששולפת את הנסיעות הפנויות מתחנה מסוימת ומציגה אותן בטבלת HTML (או הודעה, אם אין כאלה).
רעיון:
SqlConnection) נוצר עם מחרוזת ההתחברות שהתקבלה כפרמטר.'...', כי בשפת SQL ערך טקסט נכתב בתוך גרשיים. "יש מקומות פנויים" ← freeSeats > 0.SqlDataAdapter מקבל את הפקודה cmd וממלא את ה-DataSet.ds.Tables[0].Rows הוא אוסף השורות שהתקבלו; Rows.Count הוא מספרן – אם הוא 0 מציגים הודעה, אחרת בונים טבלה: <tr> לכל נסיעה ו-<td> לכל תא.Response.Write.פתרון:
| מספר | הביטוי החסר | הסבר |
|---|---|---|
| (1) | connectionString | מחרוזת ההתחברות שהפונקציה קיבלה כפרמטר |
| (2) | station | התחנה שנבחרה – משורשרת לתוך השאילתה בין הגרשיים |
| (3) | > | freeSeats > 0 – יש לפחות מקום פנוי אחד (גם <> מתקבל, כי מספר המקומות הפנויים אינו שלילי) |
| (4) | cmd | new SqlDataAdapter(cmd) – המתאם מריץ את הפקודה |
| (5) | Count | מספר השורות שהתקבלו מהשאילתה |
| (6) | Rows | מעבר על כל השורות של הטבלה הראשונה ב-DataSet |
| (7) | td | פתיחת התא של שעת היציאה (נסגר ב-</td>) |
| (8) | Write | Response.Write(res); – שליחת התוצאה לדפדפן |
הפונקציה המלאה:
protected void ShowTrips(string connectionString, string station)
{
string res = "";
DataSet ds = new DataSet();
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT tripNum, toStation, departure FROM [dbo].[Trips] WHERE fromStation = '" + station + "' AND freeSeats > 0";
cmd.Connection = conn;
conn.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ds);
conn.Close();
if (ds.Tables[0].Rows.Count == 0)
res = "אין נסיעות עם מקומות פנויים מתחנה זו";
else
{
res += "<table><tr><th>מספר נסיעה</th><th>יעד</th><th>שעת יציאה</th></tr>";
foreach (DataRow dr in ds.Tables[0].Rows)
{
res += "<tr><td>" + dr["tripNum"] + "</td><td>" + dr["toStation"] + "</td>";
res += "<td>" + dr["departure"] + "</td></tr>";
}
res += "</table>";
}
Response.Write(res);
}בדיקה: הפונקציה על טבלת הדוגמה:
... WHERE fromStation = 'עכו' AND freeSeats > 0 ומתקבלות נסיעות 311 (נהריה, 07:15) ו-315 (בית שאן, 09:30); נסיעה 312 נפסלת (0 מקומות).הפלט עבור עכו:
<table><tr><th>מספר נסיעה</th><th>יעד</th><th>שעת יציאה</th></tr><tr><td>311</td><td>נהריה</td><td>07:15</td></tr><tr><td>315</td><td>בית שאן</td><td>09:30</td></tr></table>טעות נפוצה: להשלים ב-(2) "station" (במירכאות) – אז השאילתה תחפש תחנה ששמה המילה station ולא את הערך שבמשתנה. טעות נוספת: ב-(3) >= – אז יוצגו גם נסיעות מלאות.
protected void ShowTrips(string connectionString, string station)
{
string res = "";
DataSet ds = new DataSet();
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT tripNum, toStation, departure FROM [dbo].[Trips] WHERE fromStation = '" + station + "' AND freeSeats > 0";
cmd.Connection = conn;
conn.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ds);
conn.Close();
if (ds.Tables[0].Rows.Count == 0)
res = "אין נסיעות עם מקומות פנויים מתחנה זו";
else
{
res += "<table><tr><th>מספר נסיעה</th><th>יעד</th><th>שעת יציאה</th></tr>";
foreach (DataRow dr in ds.Tables[0].Rows)
{
res += "<tr><td>" + dr["tripNum"] + "</td><td>" + dr["toStation"] + "</td>";
res += "<td>" + dr["departure"] + "</td></tr>";
}
res += "</table>";
}
Response.Write(res);
}טבלת התשובות
| שאלה | סעיף | תשובה |
|---|---|---|
| 1 | - | ב. הדפדפן ששולח בקשה לשרת ומציג למשתמש את הדף שהתקבל. |
| 2 | - | SELECT courseName FROM Courses WHERE registered < maxPlaces; |
| 3 | - |
|
| 4 | - | (1) sent.length != 2 * original.length |
| 5 | - | (1) WHERE Rain > 0 |
| 6 | - | if (Session["userName"] == null) Response.Redirect("login.aspx"); else { int rating = (int)Session["rating"]; if (rating >= 1800) { Session["group"] = "masters"; Response.Redirect("masters.aspx"); } else { Session["group"] = "open"; Response.Redirect("open.aspx"); } } |
| 7 | א | SELECT COUNT(*) FROM WeatherLog WHERE rainMm = 0 AND maxTemp >= 25; (בטבלת הדוגמה: 2) |
| ב | SELECT MIN(minTemp) FROM WeatherLog; (בטבלת הדוגמה: 7) | |
| ג | SELECT city, dayId FROM WeatherLog WHERE maxTemp - minTemp > 10; (בטבלת הדוגמה: Eilat | 2) | |
| ד | SELECT AVG(rainMm) FROM WeatherLog; (בטבלת הדוגמה: 7.2; ב-SQL Server, שבו AVG על עמודה מטיפוס מספר שלם מחזיר מספר שלם: 7) | |
| ה | SELECT dayId FROM WeatherLog WHERE windKmh < 10 OR windKmh > 40; (בטבלת הדוגמה: 2, 4, 5) | |
| ו | SELECT DISTINCT city FROM WeatherLog WHERE city LIKE '%f%'; (בטבלת הדוגמה: Haifa, Safed) | |
| 8 | א | (1) 2 (2) Lock (3) null (4) (int)Application["left"] (5) (int)Session["bought"] + 1 |
| ב | מונה משותף לכל משתמשי האתר של מספר הכרטיסים שנשארו למכירה (מתחיל ב-100 ויורד ב-1 בכל קנייה של תלמיד כלשהו). | |
| ג | מונה אישי לכל תלמיד של הכרטיסים שכבר קנה; כשהוא מגיע ל-2 התלמיד לא יכול לקנות עוד. | |
| 9 | - | (1) connectionString (2) station (3) > (4) cmd (5) Count (6) Rows (7) td (8) Write |