Menu

SQLite LIMIT ו-OFFSET: דפדוף וחיתוך של תוצאות

איך LIMIT ו-OFFSET עובדים ב-SQLite: הגבלת מספר השורות, דילוג על שורות, דפדוף בטוח ומלכודת הביצועים שכדאי להימנע ממנה בטבלאות גדולות.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

LIMIT מגביל את מספר השורות

LIMIT הוא הכפתור הפשוט ביותר ב-SQL: הוא אומר ל-SQLite "תני לי לכל היותר כך וכך שורות". שימו אותו בסוף SELECT ותקבלו בחזרה עד המספר הזה של תוצאות, לא יותר, ואולי פחות אם אין בטבלה מספיק שורות.

אתם מקבלים בחזרה את שלוש השורות הראשונות. אילו שלוש בדיוק? כאן הקאץ': בלי ORDER BY, SQLite בוחרת בכל סדר שנוח לה. היום זה אולי סדר ההכנסה; מחר, אחרי עדכון או שינוי באינדקס, אולי לא. LIMIT לבדו מתאים ל"הראו לי דוגמה", אבל ברגע שהסדר חשוב, צריך להגדיר אותו במפורש.

OFFSET מדלג על שורות מההתחלה

צרפו OFFSET ל-LIMIT ותוכלו לבקש פרוסה מאמצע קבוצת התוצאות. OFFSET k זורק את k השורות הראשונות; LIMIT n מחזיר אז עד n שורות ממה שנשאר.

זה "דלגו על שתי שורות, החזירו את השתיים הבאות": שורות 3 ו-4 בתוצאה הממוינת. המודל המנטלי: WHERE מסנן, ORDER BY ממיין, OFFSET מדלג, LIMIT מגביל. הם רצים בסדר הזה, וכולם חשובים.

דפדוף תמיד דורש ORDER BY

השימוש הנפוץ ביותר ב-LIMIT וב-OFFSET הוא דפדוף: פיצול רשימה ארוכה לעמודים של, נניח, 20 שורות כל אחד. עמוד 1 הוא LIMIT 20 OFFSET 0, עמוד 2 הוא LIMIT 20 OFFSET 20, וכן הלאה.

שני דברים לשים לב אליהם. ראשית, ה-ORDER BY לא נתון למשא ומתן: בלעדיו, ל"עמוד 2" אין משמעות מוגדרת ושורות יכולות להתערבב בין טעינות. שנית, מפתח המיון כולל את id כשובר שוויון. אם לשני פוסטים יש אותו created_at, צריך עמודה ייחודית כדי לתת להם סדר דטרמיניסטי, אחרת המיקומים שלהם יכולים להתחלף ושורה יכולה לדלוף בין עמודים.

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

קיצור: LIMIT n, m

SQLite תומכת בתחביר ישן עם פסיק, לשם תאימות לאחור עם MySQL: LIMIT offset, count. המשמעות זהה ל-LIMIT count OFFSET offset, אבל הסדר הפוך וקל לקרוא אותו לא נכון.

-- שתי אלה שקולות:
SELECT * FROM books LIMIT 10 OFFSET 20;
SELECT * FROM books LIMIT 20, 10;     -- קודם ההיסט, ואז הכמות

הצורה השנייה תמציתית, אבל היא עוקצת את מי שמצפה שהמספר הראשון יהיה הכמות. הישארו עם LIMIT n OFFSET k: זה מפורש ונקרא לפי הסדר.

OFFSET בלי LIMIT: הטריק של LIMIT -1

OFFSET לא תקף לבדו: הדקדוק של SQLite דורש שיבוא אחרי LIMIT. אז איך אומרים "דלגו על 10 השורות הראשונות ותנו לי את כל מה שאחריהן"? המוסכמה היא LIMIT -1, ש-SQLite קוראת כ"בלי גבול עליון".

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

מלכודת הביצועים של OFFSET

הנה הדבר שאף אחד לא מזכיר עד שנתקלים בו: OFFSET לא גורם ל-SQLite לדלג על עבודה, הוא גורם לה לדלג על פלט. כדי להחזיר את השורות 10,001 עד 10,020, המנוע עדיין עובר פנימית על עשרת אלפים השורות הראשונות לפני שהוא מתחיל להוציא תוצאות. היסטים קטנים הם בחינם; היסטים של עשרות או מאות אלפים נעשים איטיים באופן מורגש.

לדפדוף עמוק, הפתרון המקובל הוא keyset pagination: במקום "דלגו על N שורות", זוכרים את מפתח המיון של השורה האחרונה ומבקשים "שורות אחרי זו".

כל עמוד מבצע חיפוש באינדקס במקום לסרוק את כל מה שבא לפניו. המחיר: אי אפשר לקפוץ ל"עמוד 47", רק להתקדם קדימה בנתונים. לפידים עם גלילה אינסופית ולסמנים (cursors) של API, זה בדיוק מה שרוצים.

דפדוף מבוסס OFFSET מתאים לטבלאות ניהול ולקבוצות תוצאות קטנות. לכל דבר שגדל בלי גבול, השתמשו ב-keyset pagination.

דוגמה מלאה

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

מסננים למוצרי משרד, ממיינים לפי מחיר בסדר עולה עם השם כשובר שוויון, ולוקחים את שני הראשונים. שנו את OFFSET 0 ל-OFFSET 2 בשביל עמוד 2. השאילתה קצרה, אבל כל פסוקית בה מצדיקה את קיומה.

הצעד הבא: DISTINCT

LIMIT קובע כמה שורות חוזרות; DISTINCT קובע אם כפילויות חוזרות בכלל. זו הפסוקית הבאה בארגז הכלים של SELECT, וקל מאוד להשתמש בה לא נכון, ועל זה העמוד הבא.

שאלות נפוצות

מה עושה LIMIT ב-SQLite?

LIMIT n מגביל את מספר השורות ש-SELECT מחזיר ל-n לכל היותר. הוא רץ אחרי WHERE, GROUP BY ו-ORDER BY, כך שמגבילים את קבוצת התוצאות הסופית, לא את השורות שהשאילתה סורקת. SELECT * FROM users LIMIT 10 מחזיר עד עשר שורות.

איך OFFSET עובד יחד עם LIMIT ב-SQLite?

OFFSET k מדלג על k השורות הראשונות בתוצאה לפני ש-LIMIT מתחיל לספור. כך LIMIT 10 OFFSET 20 מחזיר את השורות 21 עד 30. SQLite עדיין צריכה לעבור על השורות שדילגו עליהן באופן פנימי, ולכן היסטים גדולים נעשים איטיים.

אפשר להשתמש ב-OFFSET בלי LIMIT ב-SQLite?

לא ישירות: OFFSET תקף רק כחלק מפסוקית LIMIT. הפתרון הוא LIMIT -1 OFFSET k, כאשר -1 פירושו 'בלי גבול עליון', כך ש-SQLite מדלגת על k שורות ומחזירה את כל מה שאחריהן. זו מוזרות שכדאי לזכור.

למה שאילתות עם דפדוף צריכות ORDER BY?

בלי ORDER BY, SQLite רשאית להחזיר שורות בכל סדר שנוח לה, והסדר הזה יכול להשתנות בין שאילתות. הדפדוף נשבר: אותה שורה יכולה להופיע בעמוד 1 ובעמוד 3, או להיעלם לגמרי. תמיד צרפו ל-LIMIT/OFFSET פסוקית ORDER BY על עמודה יציבה וייחודית.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל