سؤال

أنا مهتم بتعلم بعض الطرق الحيادية لقاعدة البيانات (المثالية) لاختيار ملف نالصف الرابع من جدول قاعدة البيانات.سيكون من المثير للاهتمام أيضًا معرفة كيف يمكن تحقيق ذلك باستخدام الوظيفة الأصلية لقواعد البيانات التالية:

  • خادم قاعدة البيانات
  • ماي إس كيو إل
  • 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;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top