كيفية تحديد الصف n في جدول قاعدة بيانات SQL؟
-
08-06-2019 - |
سؤال
أنا مهتم بتعلم بعض الطرق الحيادية لقاعدة البيانات (المثالية) لاختيار ملف نالصف الرابع من جدول قاعدة البيانات.سيكون من المثير للاهتمام أيضًا معرفة كيف يمكن تحقيق ذلك باستخدام الوظيفة الأصلية لقواعد البيانات التالية:
- خادم قاعدة البيانات
- ماي إس كيو إل
- PostgreSQL
- سكليتي
- وحي
أقوم حاليًا بشيء مثل ما يلي في SQL Server 2005، ولكنني سأكون مهتمًا برؤية الأساليب الأكثر حيادية للآخرين:
WITH Ordered AS (
SELECT ROW_NUMBER() OVER (ORDER BY OrderID) AS RowNumber, OrderID, OrderDate
FROM Orders)
SELECT *
FROM Ordered
WHERE RowNumber = 1000000
الائتمان لـ SQL أعلاه: مدونة فيروز الأنصاري
تحديث: يرى إجابة ترويلز ارفين فيما يتعلق بمعيار SQL. ترويلز، هل لديك أي روابط يمكننا ذكرها؟
المحلول
توجد طرق للقيام بذلك في الأجزاء الاختيارية من المعيار، لكن الكثير من قواعد البيانات تدعم طريقتها الخاصة للقيام بذلك.
موقع جيد حقا يتحدث عن هذا وأشياء أخرى http://troels.arvin.dk/db/rdbms/#select-limit.
في الأساس، يدعم PostgreSQL وMySQL العناصر غير القياسية:
SELECT...
LIMIT y OFFSET x
تدعم Oracle وDB2 وMSSQL وظائف النوافذ القياسية:
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
columns
FROM tablename
) AS foo
WHERE rownumber <= n
(الذي قمت بنسخه للتو من الموقع المرتبط أعلاه لأنني لا أستخدم قواعد البيانات هذه مطلقًا)
تحديث: اعتبارًا من PostgreSQL 8.4، أصبحت وظائف النوافذ القياسية مدعومة، لذا توقع أن يعمل المثال الثاني مع PostgreSQL أيضًا.
تحديث: تمت إضافة دعم وظائف النافذة لـ SQLite في الإصدار 3.25.0 بتاريخ 15/09/2018 لذا يعمل كلا النموذجين أيضًا في SQLite.
نصائح أخرى
ال LIMIT
/ OFFSET
بناء الجملة في PostgreSQL يكون:
SELECT
*
FROM
mytable
ORDER BY
somefield
LIMIT 1 OFFSET 20;
يحدد هذا المثال الصف الحادي والعشرين. OFFSET 20
يطلب من Postgres تخطي أول 20 سجلاً.إذا لم تحدد ORDER BY
بند، ليس هناك ضمان بشأن السجل الذي ستستعيده، وهو أمر نادرًا ما يكون مفيدًا.
يبدو أن معيار SQL صامت بشأن مشكلة الحد خارج وظائف النوافذ المجنونة، ولهذا السبب ينفذها الجميع بشكل مختلف.
لست متأكدًا من أي شيء آخر، لكنني أعلم أن SQLite وMySQL ليس لديهما أي ترتيب صفوف "افتراضي".في هاتين اللغتين، على الأقل، يلتقط المقتطف التالي الإدخال الخامس عشر من the_table، ويتم فرزه حسب تاريخ/وقت إضافته:
SELECT * FROM the_table ORDER BY added DESC LIMIT 1,15
(بالطبع، ستحتاج إلى إضافة حقل DATETIME، وتعيينه على تاريخ/وقت إضافة هذا الإدخال...)
يحتوي SQL 2005 والإصدارات الأحدث على هذه الميزة المضمنة.استخدم الدالة ROW_NUMBER().إنه ممتاز لصفحات الويب ذات نمط التصفح << السابق والتالي >>:
بناء الجملة:
SELECT
*
FROM
(
SELECT
ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
*
FROM
Table_1
) sub
WHERE
RowNum = 23
أظن أن هذا غير فعال إلى حد كبير ولكنه أسلوب بسيط للغاية، وقد نجح في مجموعة بيانات صغيرة قمت بتجربتها.
select top 1 field
from table
where field in (select top 5 field from table order by field asc)
order by field desc
سيؤدي هذا إلى الحصول على العنصر الخامس، وتغيير الرقم العلوي الثاني للحصول على عنصر ن مختلف
خادم SQL فقط (على ما أظن) ولكن يجب أن يعمل على الإصدارات الأقدم التي لا تدعم ROW_NUMBER().
التحقق من ذلك على SQL Server:
Select top 10 * From emp
EXCEPT
Select top 9 * From emp
سيعطيك هذا الصف العاشر من جدول emp!
1 تغيير بسيط:ن-1 بدلاً من ن.
select *
from thetable
limit n-1, 1
على عكس ما تدعي بعض الإجابات، فإن معيار SQL ليس صامتا فيما يتعلق بهذا الموضوع.
منذ SQL:2003، أصبحت قادرًا على استخدام "وظائف النافذة" لتخطي الصفوف وتحديد مجموعات النتائج.
وفي SQL:2008، تمت إضافة نهج أبسط قليلاً، باستخدام
OFFSET يتخطى ROWS
FETCH FIRST ن ROWS ONLY
شخصيًا، لا أعتقد أن إضافة SQL:2008 كانت ضرورية بالفعل، لذا لو كنت ISO، لكنت قد أبقيتها خارج نطاق المعيار الكبير بالفعل.
وحي:
select * from (select foo from bar order by foo) where ROWNUM = x
عندما كنا نعمل في MSSQL 2000، قمنا بما أسميناه "القلب الثلاثي":
تم التعديل
DECLARE @InnerPageSize int
DECLARE @OuterPageSize int
DECLARE @Count int
SELECT @Count = COUNT(<column>) FROM <TABLE>
SET @InnerPageSize = @PageNum * @PageSize
SET @OuterPageSize = @Count - ((@PageNum - 1) * @PageSize)
IF (@OuterPageSize < 0)
SET @OuterPageSize = 0
ELSE IF (@OuterPageSize > @PageSize)
SET @OuterPageSize = @PageSize
DECLARE @sql NVARCHAR(8000)
SET @sql = 'SELECT * FROM
(
SELECT TOP ' + CAST(@OuterPageSize AS nvarchar(5)) + ' * FROM
(
SELECT TOP ' + CAST(@InnerPageSize AS nvarchar(5)) + ' * FROM <TABLE> ORDER BY <column> ASC
) AS t1 ORDER BY <column> DESC
) AS t2 ORDER BY <column> ASC'
PRINT @sql
EXECUTE sp_executesql @sql
لم تكن أنيقة، ولم تكن سريعة، لكنها نجحت.
خادم قاعدة البيانات
حدد السجل n من الأعلى
SELECT * FROM (
SELECT
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID) AS ROW
FROM TABLE
) AS TMP
WHERE ROW = n
حدد السجل n من الأسفل
SELECT * FROM (
SELECT
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID DESC) AS ROW
FROM TABLE
) AS TMP
WHERE ROW = n
إليك حل سريع لارتباكك.
SELECT * FROM table ORDER BY `id` DESC LIMIT N, 1
هنا يمكنك الحصول على الصف الأخير عن طريق ملء N = 0، والثاني الأخير عن طريق N = 1، والرابع الأخير عن طريق ملء N = 3 وهكذا.
هذا سؤال شائع جدًا خلال المقابلة وهذه إجابة بسيطة جدًا عنه.
علاوة على ذلك، إذا كنت تريد المبلغ أو المعرف أو ترتيب الفرز الرقمي، فيمكنك استخدام وظيفة CAST في MySQL.
SELECT DISTINCT (`amount`) FROM cart ORDER BY CAST( `amount` AS SIGNED ) DESC LIMIT 4 , 1
هنا من خلال ملء N = 4 ستتمكن من الحصول على السجل الخامس الأخير لأعلى مبلغ من جدول CART.يمكنك ملاءمة اسم الحقل والجدول الخاص بك والتوصل إلى حل.
يضيف:
LIMIT n,1
سيؤدي ذلك إلى قصر النتائج على نتيجة واحدة تبدأ من النتيجة n.
على سبيل المثال، إذا كنت تريد تحديد كل صف عاشر في MSSQL، يمكنك استخدام؛
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY ColumnName1 ASC) AS rownumber, ColumnName1, ColumnName2
FROM TableName
) AS foo
WHERE rownumber % 10 = 0
فقط خذ MOD وقم بتغيير الرقم 10 هنا بأي رقم تريده.
LIMIT n,1 لا يعمل في MS SQL Server.أعتقد أن الأمر يتعلق فقط بقاعدة البيانات الرئيسية الوحيدة التي لا تدعم بناء الجملة هذا.لكي نكون منصفين، فهو ليس جزءًا من معيار SQL، على الرغم من أنه مدعوم على نطاق واسع لدرجة أنه ينبغي أن يكون كذلك.في كل شيء باستثناء SQL server LIMIT يعمل بشكل رائع.بالنسبة لخادم SQL، لم أتمكن من العثور على حل أنيق.
إليك إصدار عام من sproc الذي كتبته مؤخرًا لـ Oracle والذي يسمح بالترحيل/الفرز الديناميكي - HTH
-- p_LowerBound = first row # in the returned set; if second page of 10 rows,
-- this would be 11 (-1 for unbounded/not set)
-- p_UpperBound = last row # in the returned set; if second page of 10 rows,
-- this would be 20 (-1 for unbounded/not set)
OPEN o_Cursor FOR
SELECT * FROM (
SELECT
Column1,
Column2
rownum AS rn
FROM
(
SELECT
tbl.Column1,
tbl.column2
FROM MyTable tbl
WHERE
tbl.Column1 = p_PKParam OR
tbl.Column1 = -1
ORDER BY
DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 1, Column1, 'X'),'X'),
DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 1, Column1, 'X'),'X') DESC,
DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate),
DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate) DESC
))
WHERE
(rn >= p_lowerBound OR p_lowerBound = -1) AND
(rn <= p_upperBound OR p_upperBound = -1);
ولكن في الحقيقة، أليس كل هذا مجرد حيل لتصميم قاعدة بيانات جيدة في المقام الأول؟في المرات القليلة التي كنت أحتاج فيها إلى وظيفة مثل هذه، كان ذلك لاستعلام بسيط لمرة واحدة لتقديم تقرير سريع.بالنسبة لأي عمل حقيقي، فإن استخدام مثل هذه الحيل يجلب المتاعب.إذا كانت هناك حاجة إلى تحديد صف معين، فما عليك سوى إنشاء عمود بقيمة تسلسلية والانتهاء منه.
في Oracle 12c، يمكنك استخدام OFFSET..FETCH..ROWS
الخيار مع ORDER BY
على سبيل المثال، للحصول على السجل الثالث من الأعلى:
SELECT *
FROM sometable
ORDER BY column_name
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;
في Sybase SQL في أي مكان:
SELECT TOP 1 START AT n * from table ORDER BY whatever
لا تنسَ الأمر ORDER BY وإلا فإنه لا معنى له.
T-SQL - تحديد رقم السجل N من الجدول
select * from
(select row_number() over (order by Rand() desc) as Rno,* from TableName) T where T.Rno = RecordNumber
Where RecordNumber --> Record Number to Select
TableName --> To be Replaced with your Table Name
على سبيل المثال.لتحديد السجل الخامس من جدول الموظف، يجب أن يكون الاستعلام الخاص بك
select * from
(select row_number() over (order by Rand() desc) as Rno,* from Employee) T where T.Rno = 5
SELECT * FROM emp a
WHERE n = (SELECT COUNT( _rowid)
FROM emp b
WHERE a. _rowid >= b. _rowid);
SELECT
top 1 *
FROM
table_name
WHERE
column_name IN (
SELECT
top N column_name
FROM
TABLE
ORDER BY
column_name
)
ORDER BY
column_name DESC
لقد كتبت هذا الاستعلام للعثور على الصف N.المثال مع هذا الاستعلام سيكون
SELECT
top 1 *
FROM
Employee
WHERE
emp_id IN (
SELECT
top 7 emp_id
FROM
Employee
ORDER BY
emp_id
)
ORDER BY
emp_id DESC
بالنسبة لـ SQL Server، الطريقة العامة لمعرفة رقم الصف هي كما يلي:
SET ROWCOUNT @row --@row = the row number you wish to work on.
على سبيل المثال:
set rowcount 20 --sets row to 20th row
select meat, cheese from dbo.sandwich --select columns from table at 20th row
set rowcount 0 --sets rowcount back to all rows
سيؤدي هذا إلى إرجاع معلومات الصف العشرين.تأكد من وضع عدد الصفوف 0 بعد ذلك.
لا يصدق أنه يمكنك العثور على محرك SQL ينفذ هذا المحرك ...
WITH sentence AS
(SELECT
stuff,
row = ROW_NUMBER() OVER (ORDER BY Id)
FROM
SentenceType
)
SELECT
sen.stuff
FROM sentence sen
WHERE sen.row = (ABS(CHECKSUM(NEWID())) % 100) + 1
لا شيء فاخر، ولا وظائف خاصة، في حالة استخدام ذاكرة التخزين المؤقت كما أفعل أنا...
SELECT TOP 1 * FROM (
SELECT TOP n * FROM <table>
ORDER BY ID Desc
)
ORDER BY ID ASC
نظرًا لأن لديك عمود معرف أو عمود طابع تاريخ يمكنك الوثوق به.
هذه هي الطريقة التي سأفعل بها ذلك ضمن DB2 SQL، وأعتقد أن RRN (رقم السجل النسبي) يتم تخزينه داخل الجدول بواسطة O/S؛
SELECT * FROM (
SELECT RRN(FOO) AS RRN, FOO.*
FROM FOO
ORDER BY RRN(FOO)) BAR
WHERE BAR.RRN = recordnumber
select * from
(select * from ordered order by order_id limit 100) x order by
x.order_id desc limit 1;
قم أولاً بتحديد أفضل 100 صف بالترتيب التصاعدي، ثم حدد الصف الأخير بالترتيب التنازلي والحد من 1.ومع ذلك، يعد هذا بيانًا مكلفًا للغاية لأنه يصل إلى البيانات مرتين.
يبدو لي أنه لكي تكون فعالاً، فأنت بحاجة إلى 1) إنشاء رقم عشوائي يتراوح بين 0 وواحد أقل من عدد سجلات قاعدة البيانات، و2) أن تكون قادرًا على تحديد الصف في هذا الموضع.لسوء الحظ، تحتوي قواعد البيانات المختلفة على مولدات أرقام عشوائية مختلفة وطرق مختلفة لتحديد صف في موضع ما في مجموعة النتائج - عادةً ما تحدد عدد الصفوف التي تريد تخطيها وعدد الصفوف التي تريدها، ولكن يتم ذلك بشكل مختلف بالنسبة لقواعد البيانات المختلفة.إليك شيء يناسبني في SQLite:
select *
from Table
limit abs(random()) % (select count(*) from Words), 1;
إنه يعتمد على القدرة على استخدام استعلام فرعي في جملة الحد (والتي تكون في SQLite LIMIT <recs to Skip>،<recs to take>) يجب أن يكون تحديد عدد السجلات في الجدول فعالاً بشكل خاص، كونه جزءًا من قاعدة البيانات بيانات التعريف، ولكن ذلك يعتمد على تنفيذ قاعدة البيانات.أيضًا، لا أعرف ما إذا كان الاستعلام سيبني بالفعل مجموعة النتائج قبل استرداد السجل N، ولكنني آمل ألا يحتاج إلى ذلك.لاحظ أنني لا أحدد عبارة "الترتيب حسب".قد يكون من الأفضل "الترتيب حسب" شيء مثل المفتاح الأساسي، الذي سيكون له فهرس - قد يكون الحصول على السجل N من الفهرس أسرع إذا لم تتمكن قاعدة البيانات من الحصول على السجل N من قاعدة البيانات نفسها دون إنشاء مجموعة النتائج .
بالنسبة لخادم SQL، سيعيد ما يلي الصف الأول من جدول العطاء.
declare @rowNumber int = 1;
select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
select TOP(@rowNumber - 1) * from [dbo].[someTable];
يمكنك تكرار القيم بشيء مثل هذا:
WHILE @constVar > 0
BEGIN
declare @rowNumber int = @consVar;
select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
select TOP(@rowNumber - 1) * from [dbo].[someTable];
SET @constVar = @constVar - 1;
END;