سؤال

طاولة:

UserId, Value, Date.

أريد الحصول على معرف المستخدم، قيمة الحد الأقصى (التاريخ) لكل معرف مستخدم.أي قيمة كل معرف مستخدم له التاريخ الأحدث.هل هناك طريقة للقيام بذلك ببساطة في SQL؟(يفضل أوراكل)

تحديث: نعتذر عن أي غموض:أحتاج إلى الحصول على جميع معرفات المستخدمين.ولكن بالنسبة لكل معرف مستخدم، فقط هذا الصف الذي يحتوي على أحدث تاريخ لهذا المستخدم.

هل كانت مفيدة؟

المحلول

سيؤدي هذا إلى استرداد جميع الصفوف التي تساوي قيمة عمود my_date لها الحد الأقصى لقيمة my_date لمعرف المستخدم هذا.قد يؤدي هذا إلى استرداد صفوف متعددة لمعرف المستخدم حيث يكون الحد الأقصى للتاريخ في صفوف متعددة.

select userid,
       my_date,
       ...
from
(
select userid,
       my_date,
       ...
       max(my_date) over (partition by userid) max_my_date
from   users
)
where my_date = max_my_date

"الوظائف التحليلية صخرة"

يحرر:بالنسبه للتعليق الأول...

"استخدام الاستعلامات التحليلية والانضمام الذاتي يبطل غرض الاستعلامات التحليلية"

لا يوجد انضمام ذاتي في هذا الرمز.بدلاً من ذلك، يوجد مسند يتم وضعه على نتيجة العرض المضمن الذي يحتوي على الوظيفة التحليلية - وهو أمر مختلف تمامًا، وممارسة قياسية تمامًا.

"النافذة الافتراضية في Oracle هي من الصف الأول في القسم إلى الصف الحالي"

لا ينطبق شرط النوافذ إلا في حالة وجود الأمر حسب البند.مع عدم وجود ترتيب حسب العبارة، لا يتم تطبيق أي شرط نافذة بشكل افتراضي ولا يمكن تحديد أي منها بشكل صريح.

الكود يعمل.

نصائح أخرى

أرى العديد من الأشخاص يستخدمون الاستعلامات الفرعية أو ميزات خاصة بالبائعين للقيام بذلك، ولكن غالبًا ما أقوم بهذا النوع من الاستعلامات بدون استعلامات فرعية بالطريقة التالية.يستخدم SQL قياسيًا عاديًا لذا يجب أن يعمل في أي علامة تجارية لنظام RDBMS.

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;

بعبارة أخرى:جلب الصف من t1 حيث لا يوجد صف آخر بنفس الشيء UserId وتاريخ أعظم.

(أضع المعرف "التاريخ" في المحددات لأنها كلمة محجوزة في SQL.)

في حالة إذا t1."Date" = t2."Date", ، تظهر المضاعفة.عادة ما تحتوي الجداول auto_inc(seq) المفتاح، على سبيل المثال. id.لتجنب المضاعفة يمكن استخدام ما يلي:

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date") 
         OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;

إعادة تعليق من @Farhan:

وهنا شرح أكثر تفصيلا:

صلة خارجية تحاول الانضمام t1 مع t2.بشكل افتراضي، جميع نتائج t1 يتم إرجاعها، و لو هناك مباراة في t2, ، يتم إرجاعه أيضًا.إذا لم يكن هناك تطابق في t2 لصف معين من t1, ، فسيظل الاستعلام يُرجع الصف t1, ، واستخدامات NULL كعنصر نائب لجميع t2أعمدة.هذه هي الطريقة التي تعمل بها الصلات الخارجية بشكل عام.

الحيلة في هذا الاستعلام هي تصميم حالة مطابقة الصلة بحيث t2 يجب أن تتطابق مع نفس userid, ، و أ أكبر date.الفكرة هي إذا كان هناك صف في t2 الذي لديه أكبر date, ، ثم الصف في t1 انها مقارنة ضد لا أستطيع يكون أعظم date من أجل هذا userid.ولكن إذا لم يكن هناك تطابق - أي.إذا لم يكن هناك أي صف في t2 مع أكبر date من الصف في t1 -- نحن نعلم أن الصف في t1 وكان الصف مع أعظم date للمعطى userid.

في تلك الحالات (عندما لا يكون هناك تطابق)، فإن أعمدة t2 سوف يكون NULL - حتى الأعمدة المحددة في شرط الانضمام.ولهذا السبب نستخدم WHERE t2.UserId IS NULL, ، لأننا نبحث عن الحالات التي لم يتم العثور فيها على صف يحتوي على أكبر date للمعطى userid.

SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid

لا أعرف أسماء الأعمدة الخاصة بك بالضبط، ولكن سيكون الأمر كالتالي:

    select userid, value
      from users u1
     where date = (select max(date)
                     from users u2
                    where u1.userid = u2.userid)

نظرًا لعدم وجودي في العمل، ليس لدي Oracle في متناول اليد، ولكن يبدو أنني أتذكر أن Oracle تسمح بمطابقة أعمدة متعددة في جملة IN، والتي يجب على الأقل تجنب الخيارات التي تستخدم استعلامًا فرعيًا مرتبطًا، وهو أمر نادرًا ما يكون جيدًا فكرة.

ربما شيء من هذا القبيل (لا أتذكر ما إذا كان ينبغي وضع قائمة الأعمدة بين قوسين أم لا):

SELECT * 
FROM MyTable
WHERE (User, Date) IN
  ( SELECT User, MAX(Date) FROM MyTable GROUP BY User)

يحرر:فقط جربته بشكل حقيقي:

SQL> create table MyTable (usr char(1), dt date);
SQL> insert into mytable values ('A','01-JAN-2009');
SQL> insert into mytable values ('B','01-JAN-2009');
SQL> insert into mytable values ('A', '31-DEC-2008');
SQL> insert into mytable values ('B', '31-DEC-2008');
SQL> select usr, dt from mytable
  2  where (usr, dt) in 
  3  ( select usr, max(dt) from mytable group by usr)
  4  /

U DT
- ---------
A 01-JAN-09
B 01-JAN-09

وهكذا ينجح الأمر، على الرغم من أن بعض العناصر الجديدة المذكورة في مكان آخر قد تكون أكثر أداءً.

أعلم أنك طلبت Oracle، ولكن في SQL 2005 نستخدم الآن هذا:


-- Single Value
;WITH ByDate
AS (
SELECT UserId, Value, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) RowNum
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE RowNum = 1

-- Multiple values where dates match
;WITH ByDate
AS (
SELECT UserId, Value, RANK() OVER (PARTITION BY UserId ORDER BY Date DESC) Rnk
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE Rnk = 1

ألن تكون جملة QUALIFY هي الأبسط والأفضل؟

select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

بالنسبة للسياق، في Teradata هنا، يتم تشغيل اختبار الحجم المناسب لهذا في 17 ثانية مع إصدار QUALIFY وفي 23 ثانية مع "العرض المضمن"/حل Aldridge رقم 1.

ليس لدي Oracle لاختباره، ولكن الحل الأكثر فعالية هو استخدام الاستعلامات التحليلية.يجب أن يبدو مثل هذا:

SELECT DISTINCT
    UserId
  , MaxValue
FROM (
    SELECT UserId
      , FIRST (Value) Over (
          PARTITION BY UserId
          ORDER BY Date DESC
        ) MaxValue
    FROM SomeTable
  )

أظن أنه يمكنك التخلص من الاستعلام الخارجي ووضع الاستعلام الداخلي مميزًا، لكنني لست متأكدًا.في هذه الأثناء أعلم أن هذا يعمل.

إذا كنت تريد التعرف على الاستعلامات التحليلية، أقترح عليك القراءة http://www.orafaq.com/node/55 و http://www.akadia.com/services/ora_analytic_functions.html.هنا ملخص قصير.

تحت غطاء محرك السيارة، تقوم الاستعلامات التحليلية بفرز مجموعة البيانات بأكملها، ثم معالجتها بالتسلسل.أثناء معالجتك، تقوم بتقسيم مجموعة البيانات وفقًا لمعايير معينة، ثم لكل صف تنظر إلى بعض النوافذ (القيمة الافتراضية هي القيمة الأولى في القسم إلى الصف الحالي - وهذا الإعداد الافتراضي هو أيضًا الأكثر كفاءة) ويمكن حساب القيم باستخدام عدد الوظائف التحليلية (التي تشبه قائمتها إلى حد كبير الوظائف المجمعة).

في هذه الحالة، إليك ما يفعله الاستعلام الداخلي.يتم فرز مجموعة البيانات بأكملها حسب معرف المستخدم ثم التاريخ DESC.ثم يقوم بمعالجتها في مسار واحد.لكل صف، تقوم بإرجاع معرف المستخدم وأول تاريخ تمت رؤيته لمعرف المستخدم هذا (نظرًا لأنه يتم فرز التواريخ DESC، فهذا هو التاريخ الأقصى).يمنحك هذا إجابتك بصفوف مكررة.ثم يتم سحق التكرارات الخارجية DISTINCT.

هذا ليس مثالًا رائعًا بشكل خاص للاستعلامات التحليلية.لتحقيق فوز أكبر بكثير، فكر في أخذ جدول بالإيصالات المالية وحساب إجمالي ما دفعوه لكل مستخدم وإيصال.الاستعلامات التحليلية تحل ذلك بكفاءة.الحلول الأخرى أقل كفاءة.ولهذا السبب فهي جزء من معيار SQL لعام 2003.(لسوء الحظ لا يمتلكها Postgres بعد.غرر...)

في Oracle 12c+, ، يمكنك استخدام أعلى ن الاستعلامات جنبا إلى جنب مع وظيفة التحليلية rank لتحقيق ذلك بإيجاز شديد بدون الاستعلامات الفرعية:

select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;

ما ورد أعلاه يُرجع جميع الصفوف بحد أقصى my_date لكل مستخدم.

إذا كنت تريد صفًا واحدًا فقط بحد أقصى للتاريخ، فاستبدل rank مع row_number:

select *
from your_table
order by row_number() over (partition by user_id order by my_date desc)
fetch first 1 row with ties; 

مع PostgreSQL 8.4 أو الأحدث، يمكنك استخدام ما يلي:

select user_id, user_value_1, user_value_2
  from (select user_id, user_value_1, user_value_2, row_number()
          over (partition by user_id order by user_date desc) 
        from users) as r
  where r.row_number=1

يستخدم ROW_NUMBER() لتعيين تصنيف فريد على التنازلي Date لكل UserId, ، ثم قم بالتصفية إلى الصف الأول لكل منها UserId (أي.، ROW_NUMBER = 1).

SELECT UserId, Value, Date
FROM (SELECT UserId, Value, Date,
        ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) rn
      FROM users) u
WHERE rn = 1;
Select  
   UserID,  
   Value,  
   Date  
From  
   Table,  
   (  
      Select  
          UserID,  
          Max(Date) as MDate  
      From  
          Table  
      Group by  
          UserID  
    ) as subQuery  
Where  
   Table.UserID = subQuery.UserID and  
   Table.Date = subQuery.mDate  

كان علي فقط أن أكتب مثالاً "مباشرًا" في العمل :)

يدعم هذا القيم المتعددة لمعرف المستخدم على ملف نفس تاريخ.

الأعمدة:معرف المستخدم، القيمة، التاريخ

SELECT
   DISTINCT UserId,
   MAX(Date) OVER (PARTITION BY UserId ORDER BY Date DESC),
   MAX(Values) OVER (PARTITION BY UserId ORDER BY Date DESC)
FROM
(
   SELECT UserId, Date, SUM(Value) As Values
   FROM <<table_name>>
   GROUP BY UserId, Date
)

يمكنك استخدام FIRST_VALUE بدلاً من MAX والبحث عنه في خطة الشرح.لم يكن لدي الوقت للعب معها.

بالطبع، إذا كنت تبحث من خلال جداول ضخمة، فمن الأفضل أن تستخدم تلميحات كاملة في الاستعلام الخاص بك.

select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))

أعتقد شيئا من هذا القبيل.(سامحني على أي أخطاء في بناء الجملة؛أنا معتاد على استخدام HQL في هذه المرحلة!)

يحرر:كما أخطأت في قراءة السؤال!تم تصحيح الاستفسار...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)

الشيء الوحيد الذي يجب عليك فعله هو إجراء هذا المتغير للاستعلام السابق:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

(T-SQL) احصل أولاً على جميع المستخدمين وتاريخهم الأقصى.انضم إلى الجدول للعثور على القيم المقابلة للمستخدمين في maxdates.

create table users (userid int , value int , date datetime)
insert into users values (1, 1, '20010101')
insert into users values (1, 2, '20020101')
insert into users values (2, 1, '20010101')
insert into users values (2, 3, '20030101')

select T1.userid, T1.value, T1.date 
    from users T1,
    (select max(date) as maxdate, userid from users group by userid) T2    
    where T1.userid= T2.userid and T1.date = T2.maxdate

نتائج:

userid      value       date                                    
----------- ----------- -------------------------- 
2           3           2003-01-01 00:00:00.000
1           2           2002-01-01 00:00:00.000

الجواب هنا هو أوراكل فقط.إليك إجابة أكثر تعقيدًا في جميع SQL:

من حصل على أفضل نتيجة إجمالية للواجب المنزلي (الحد الأقصى لمجموع نقاط الواجب المنزلي)؟

SELECT FIRST, LAST, SUM(POINTS) AS TOTAL
FROM STUDENTS S, RESULTS R
WHERE S.SID = R.SID AND R.CAT = 'H'
GROUP BY S.SID, FIRST, LAST
HAVING SUM(POINTS) >= ALL (SELECT SUM (POINTS)
FROM RESULTS
WHERE CAT = 'H'
GROUP BY SID)

ومثال أكثر صعوبة، والذي يحتاج إلى بعض الشرح، والذي لا أملك الوقت الكافي له:

قم بإعطاء الكتاب (رقم ISBN والعنوان) الأكثر شهرة في عام 2008، أي الذي تم استعارته في أغلب الأحيان في عام 2008.

SELECT X.ISBN, X.title, X.loans
FROM (SELECT Book.ISBN, Book.title, count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title) X
HAVING loans >= ALL (SELECT count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title);

آمل أن يساعد هذا (أي شخص)..:)

التحيات ، guus

بافتراض أن التاريخ فريد لمعرف مستخدم معين، إليك بعض TSQL:

SELECT 
    UserTest.UserID, UserTest.Value
FROM UserTest
INNER JOIN
(
    SELECT UserID, MAX(Date) MaxDate
    FROM UserTest
    GROUP BY UserID
) Dates
ON UserTest.UserID = Dates.UserID
AND UserTest.Date = Dates.MaxDate 

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

select
    userid,
    to_number(substr(max(to_char(date,'yyyymmdd') || to_char(value)), 9)) as value,
    max(date) as date
from 
    users
group by
    userid

السبب وراء نجاح هذا الرمز هو أنه يحتاج فقط إلى فحص الجدول مرة واحدة.ولا يتطلب الأمر أي فهارس، والأهم من ذلك أنه لا يحتاج إلى فرز الجدول، وهو ما تفعله معظم وظائف التحليلات.ستساعدك الفهارس إذا كنت بحاجة إلى تصفية النتيجة لمعرف مستخدم واحد.

select userid, value, date
  from thetable t1 ,
       ( select t2.userid, max(t2.date) date2 
           from thetable t2 
          group by t2.userid ) t3
 where t3.userid t1.userid and
       t3.date2 = t1.date

IMHO يعمل هذا.هث

وأعتقد أن هذا يجب أن تعمل؟

Select
T1.UserId,
(Select Top 1 T2.Value From Table T2 Where T2.UserId = T1.UserId Order By Date Desc) As 'Value'
From
Table T1
Group By
T1.UserId
Order By
T1.UserId

حاول أولاً أن أخطئ في قراءة السؤال، وبعد الإجابة العليا، إليك مثال كامل بالنتائج الصحيحة:

CREATE TABLE table_name (id int, the_value varchar(2), the_date datetime);

INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'a','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'b','2/2/2002');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'c','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'d','3/3/2003');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'e','3/3/2003');

--

  select id, the_value
      from table_name u1
      where the_date = (select max(the_date)
                     from table_name u2
                     where u1.id = u2.id)

--

id          the_value
----------- ---------
2           d
2           e
1           b

(3 row(s) affected)

سيهتم هذا أيضًا بالتكرارات (إرجاع صف واحد لكل user_id):

SELECT *
FROM (
  SELECT u.*, FIRST_VALUE(u.rowid) OVER(PARTITION BY u.user_id ORDER BY u.date DESC) AS last_rowid
  FROM users u
) u2
WHERE u2.rowid = u2.last_rowid

لقد اختبرت هذا للتو ويبدو أنه يعمل على جدول التسجيل

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc

يجب أن يكون هذا بسيطًا مثل:

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)

إذا كنت تستخدم Postgres، فيمكنك استخدام array_agg يحب

SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid

أنا لست على دراية بأوراكل.هذا هو ما خطرت لي

SELECT 
  userid,
  MAX(adate),
  SUBSTR(
    (LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)),
    0,
    INSTR((LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)), ',')-1
  ) as value 
FROM YOURTABLE
GROUP BY userid 

يعرض كلا الاستعلامين نفس نتائج الإجابة المقبولة.راجع SQLFiddles:

  1. إجابة مقبولة
  2. الحل الخاص بي مع Postgres
  3. الحل الخاص بي مع أوراكل

إذا كان (معرف المستخدم، التاريخ) فريدًا، أي.لا يظهر أي تاريخ مرتين لنفس المستخدم ثم:

select TheTable.UserID, TheTable.Value
from TheTable inner join (select UserID, max([Date]) MaxDate
                          from TheTable
                          group by UserID) UserMaxDate
     on TheTable.UserID = UserMaxDate.UserID
        TheTable.[Date] = UserMaxDate.MaxDate;
select   UserId,max(Date) over (partition by UserId) value from users;

حل لـ MySQL الذي لا يحتوي على مفاهيم القسم KEEP، DENSE_RANK.

select userid,
       my_date,
       ...
from
(
select @sno:= case when @pid<>userid then 0
                    else @sno+1
    end as serialnumber, 
    @pid:=userid,
       my_Date,
       ...
from   users order by userid, my_date
) a
where a.serialnumber=0

مرجع: http://benincampus.blogspot.com/2013/08/select-rows-what-have-maxmin-value-in.html

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top