تحديد SQL:دمج البيانات وتجميعها بين ثلاثة جداول باستخدام الاستعلامات الفرعية

StackOverflow https://stackoverflow.com/questions/809056

  •  03-07-2019
  •  | 
  •  

سؤال

آسف على السؤال الطويل وليس العنوان وصفيًا جدًا، لكن مشكلتي صعبة جدًا أن أشرحها باختصار.

لدي ثلاثة جداول في قاعدة البيانات:

TABLE A:  
AID PK  
STATUS VARCHAR

TABLE B:  
BID PK  
AID FK  
CID FK

TABLE C:  
CID PK  
CREATIONTIME DATE

لكل صف STATUS = 'OK' في الجدول A أريد العثور على الصف المقابل في C الذي يحتوي على أحدث وقت للإنشاء.

يمكنني أولاً جلب جميع الصفوف من الجدول A حيث الحالة = "موافق".
بعد ذلك يمكنني جلب كافة الصفوف المقابلة من الجدول B.
ولكن كيف تستمر من هناك؟

على سبيل المثال:

select AID, CID from B where AID in (select AID from A where STATUS = 'OK')

يمكن أن يعود شيء مثل:

AID, CID  
1    1  
2    2  
2    3  
3    4  
4    5  
4    6  

لنفترض أن CID 2 لديه وقت إنشاء أحدث من CID 3 وأن CID 6 أحدث من CID 5.وهذا يعني أن النتيجة الصحيحة ستكون الصفوف 1 و2 و4 و6 في الجدول C.

هل هناك طريقة للتعبير عن ذلك من خلال الاستعلام؟

يحرر:آسف لأنني لم أكن محددا بما فيه الكفاية.ما أريد الحصول عليه هو أرقام التعريف الجمركية (CIDs) من الجدول C.

يحرر:لقد أحصيت الصفوف التي تم إرجاعها مع الحلول المختلفة.وكانت النتائج مثيرة للاهتمام للغاية - ومتنوعة:
هينستك:298473 صفًا
جموتشيلو:298473 صفًا
كاميرا روس:290121 صفًا
كريس:344093 صفًا
الديناصورات:290119 صفًا

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

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

المحلول

شيء من هذا القبيل، إذا كنت قد فهمتك بشكل صحيح

SELECT
    MAX(CREATIONTIME),
    A.AID
FROM
    A
INNER JOIN
    B
    ON 
    A.AID = B.AID
INNER JOIN
    C
    ON 
    B.CID = C.CID
WHERE
    A.STATUS = 'OK'
GROUP BY
    A.AID

يحرر:

لقد قمت الآن بالتحقق مما يلي في SQL Server (أود أن أعرض نفس النتيجة في Oracle) وتقوم بإرجاع ملف CID ل C سجل مع الحد الأقصى CREATIONTIME أين ال STATUS للسجل ذي الصلة في A بطاقة تعريف 'OK'.

SELECT C.CID
FROM 
C C
INNER JOIN
B B
ON 
C.CID = B.CID
INNER JOIN
(
    SELECT
        MAX(C.CREATIONTIME) CREATIONTIME,
        A.AID
    FROM
        A A
    INNER JOIN
        B B
        ON 
        A.AID = B.AID
    INNER JOIN
        C C
        ON 
        B.CID = C.CID
    WHERE
        A.STATUS = 'OK'
    GROUP BY
        A.AID
) ABC
ON B.AID = ABC.AID
AND C.CREATIONTIME = ABC.CREATIONTIME

تبين مع ما يلي تي إس كيو إل

DECLARE @A TABLE(AID INT IDENTITY(1,1), STATUS VARCHAR(10))
DECLARE @B TABLE(BID INT IDENTITY(1,1), AID INT, CID INT)
DECLARE @C TABLE(CID INT IDENTITY(1,1), CREATIONTIME DATETIME)

INSERT INTO @A VALUES ('OK')
INSERT INTO @A VALUES ('OK')
INSERT INTO @A VALUES ('NOT OK')
INSERT INTO @A VALUES ('OK')
INSERT INTO @A VALUES ('NOT OK')

INSERT INTO @C VALUES ('10 MAR 2008')
INSERT INTO @C VALUES ('13 MAR 2008')
INSERT INTO @C VALUES ('15 MAR 2008')
INSERT INTO @C VALUES ('17 MAR 2008')
INSERT INTO @C VALUES ('21 MAR 2008')

INSERT INTO @B VALUES (1,1)
INSERT INTO @B VALUES (1,2)
INSERT INTO @B VALUES (1,3)
INSERT INTO @B VALUES (2,2)
INSERT INTO @B VALUES (2,3)
INSERT INTO @B VALUES (2,4)
INSERT INTO @B VALUES (3,3)
INSERT INTO @B VALUES (3,4)
INSERT INTO @B VALUES (3,5)
INSERT INTO @B VALUES (4,5)
INSERT INTO @B VALUES (4,1)
INSERT INTO @B VALUES (4,2)


SELECT C.CID
FROM 
@C C
INNER JOIN
@B B
ON 
C.CID = B.CID
INNER JOIN
(
SELECT
    MAX(C.CREATIONTIME) CREATIONTIME,
    A.AID
FROM
    @A A
INNER JOIN
    @B B
    ON 
    A.AID = B.AID
INNER JOIN
    @C C
    ON 
    B.CID = C.CID
WHERE
    A.STATUS = 'OK'
GROUP BY
    A.AID
) ABC
ON B.AID = ABC.AID
AND C.CREATIONTIME = ABC.CREATIONTIME

النتائج في ما يلي

CID
-----------
3
4
5

تحرير 2:

ردًا على تعليقك حول كل عبارة تعطي نتائج مختلفة، قمت بتشغيل بعض الإجابات المختلفة هنا من خلال SQL Server 2005 باستخدام بيانات الاختبار الخاصة بي أعلاه (أقدر أنك تستخدم Oracle).وهنا النتائج

--Expected results for CIDs would be

--CID
-----------
--3
--4
--5

--As indicated in the comments next to the insert statements

DECLARE @A TABLE(AID INT IDENTITY(1,1), STATUS VARCHAR(10))
DECLARE @B TABLE(BID INT IDENTITY(1,1), AID INT, CID INT)
DECLARE @C TABLE(CID INT IDENTITY(1,1), CREATIONTIME DATETIME)

INSERT INTO @A VALUES ('OK') -- AID 1
INSERT INTO @A VALUES ('OK') -- AID 2
INSERT INTO @A VALUES ('NOT OK')
INSERT INTO @A VALUES ('OK') -- AID 4
INSERT INTO @A VALUES ('NOT OK')

INSERT INTO @C VALUES ('10 MAR 2008')
INSERT INTO @C VALUES ('13 MAR 2008')
INSERT INTO @C VALUES ('15 MAR 2008')
INSERT INTO @C VALUES ('17 MAR 2008')
INSERT INTO @C VALUES ('21 MAR 2008')

INSERT INTO @B VALUES (1,1)
INSERT INTO @B VALUES (1,2)
INSERT INTO @B VALUES (1,3) -- Will be CID 3 For AID 1
INSERT INTO @B VALUES (2,2)
INSERT INTO @B VALUES (2,3)
INSERT INTO @B VALUES (2,4) -- Will be CID 4 For AID 2
INSERT INTO @B VALUES (3,3)
INSERT INTO @B VALUES (3,4)
INSERT INTO @B VALUES (3,5)
INSERT INTO @B VALUES (4,5) -- Will be CID 5 FOR AID 4
INSERT INTO @B VALUES (4,1)
INSERT INTO @B VALUES (4,2)

-- Russ Cam
SELECT C.CID, ABC.CREATIONTIME
FROM 
@C C
INNER JOIN
@B B
ON 
C.CID = B.CID
INNER JOIN
(
SELECT
    MAX(C.CREATIONTIME) CREATIONTIME,
    A.AID
FROM
    @A A
INNER JOIN
    @B B
    ON 
    A.AID = B.AID
INNER JOIN
    @C C
    ON 
    B.CID = C.CID
WHERE
    A.STATUS = 'OK'
GROUP BY
    A.AID
) ABC
ON B.AID = ABC.AID
AND C.CREATIONTIME = ABC.CREATIONTIME

-- Tyrannosaurs
select   A.AID,  
         max(AggC.CREATIONTIME)  
from    @A A,  
         @B B,  
         (  select  C.CID,  
             max(C.CREATIONTIME) CREATIONTIME  
            from @C C  
            group by CID
          ) AggC  
where    A.AID = B.AID  
and    B.CID = AggC.CID  
and    A.Status = 'OK'  
group by A.AID

-- jmucchiello
SELECT c.cid, max(c.creationtime)
FROM @B b, @C c
WHERE b.cid = c.cid
 AND b.aid IN (SELECT a.aid FROM @A a WHERE status = 'OK')
GROUP BY c.cid

-- hainstech
SELECT agg.aid, agg.cid
FROM (
    SELECT a.aid
        ,c.cid
        ,max(c.creationtime) as maxcCreationTime
    FROM @C c INNER JOIN @B b ON b.cid = c.cid
        INNER JOIN @A a on a.aid = b.aid
    WHERE a.status = 'OK'
    GROUP BY a.aid, c.cid
) as agg

--chris
SELECT A.AID, C.CID, C.CREATIONTIME
FROM @A A, @B B, @C C
WHERE A.STATUS = 'OK'
AND A.AID = B.AID
AND B.CID = C.CID
AND C.CREATIONTIME = 
(SELECT MAX(C2.CREATIONTIME) 
FROM @C C2, @B B2 
WHERE B2.AID = A.AID
AND C2.CID = B2.CID);

النتائج كما يلي

--Russ Cam - Correct CIDs (I have added in the CREATIONTIME for reference)
CID         CREATIONTIME
----------- -----------------------
3           2008-03-15 00:00:00.000
4           2008-03-17 00:00:00.000
5           2008-03-21 00:00:00.000

--Tyrannosaurs - No CIDs in the resultset
AID         
----------- -----------------------
1           2008-03-15 00:00:00.000
2           2008-03-17 00:00:00.000
4           2008-03-21 00:00:00.000


--jmucchiello - Incorrect CIDs in the resultset
cid         
----------- -----------------------
1           2008-03-10 00:00:00.000
2           2008-03-13 00:00:00.000
3           2008-03-15 00:00:00.000
4           2008-03-17 00:00:00.000
5           2008-03-21 00:00:00.000

--hainstech - Too many CIDs in the resultset, which CID has the MAX(CREATIONTIME) for each AID?
aid         cid
----------- -----------
1           1
1           2
1           3
2           2
2           3
2           4
4           1
4           2
4           5

--chris - Correct CIDs, it is the same SQL as mine
AID         CID         CREATIONTIME
----------- ----------- -----------------------
1           3           2008-03-15 00:00:00.000
2           4           2008-03-17 00:00:00.000
4           5           2008-03-21 00:00:00.000

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

نصائح أخرى

SQL> create table a (aid,status)
  2  as
  3  select 1, 'OK' from dual union all
  4  select 2, 'OK' from dual union all
  5  select 3, 'OK' from dual union all
  6  select 4, 'OK' from dual union all
  7  select 5, 'NOK' from dual
  8  /

Tabel is aangemaakt.

SQL> create table c (cid,creationtime)
  2  as
  3  select 1, sysdate - 1 from dual union all
  4  select 2, sysdate - 2 from dual union all
  5  select 3, sysdate - 3 from dual union all
  6  select 4, sysdate - 4 from dual union all
  7  select 5, sysdate - 6 from dual union all
  8  select 6, sysdate - 5 from dual
  9  /

Tabel is aangemaakt.

SQL> create table b (bid,aid,cid)
  2  as
  3  select 1, 1, 1 from dual union all
  4  select 2, 2, 2 from dual union all
  5  select 3, 2, 3 from dual union all
  6  select 4, 3, 4 from dual union all
  7  select 5, 4, 5 from dual union all
  8  select 6, 4, 6 from dual union all
  9  select 7, 5, 6 from dual
 10  /

Tabel is aangemaakt.

SQL> select a.aid
  2       , max(c.cid) keep (dense_rank last order by c.creationtime) cid
  3       , max(c.creationtime) creationtime
  4    from a
  5       , b
  6       , c
  7   where b.aid = a.aid
  8     and b.cid = c.cid
  9     and a.status = 'OK'
 10   group by a.aid
 11  /

       AID        CID CREATIONTIME
---------- ---------- -------------------
         1          1 30-04-2009 09:26:00
         2          2 29-04-2009 09:26:00
         3          4 27-04-2009 09:26:00
         4          6 26-04-2009 09:26:00

4 rijen zijn geselecteerd.

وحدد الحقل الذي تبحث عنه باستخدام انضمام جميع الجداول 3 ومن ثم الحد من النتائج لتلك حيث CREATIONDATE هو الأحدث.

SELECT A.AID, C.CID, C.CREATIONTIME
FROM A A, B B, C C
WHERE A.STATUS = 'OK'
AND A.AID = B.AID
AND B.CID = C.CID
AND C.CREATIONTIME = 
(SELECT MAX(C2.CREATIONTIME) 
FROM C C2, B B2 
WHERE B2.AID = A.AID
AND C2.CID = B2.CID);

وتحرير: وكان جوابي السابقة هراء. هذا هو الآن إعادة كتابة كاملة

وهذا هو في الواقع مشكلة التي تنصتت لي طوال حياتي SQL الحياة. الحل انا ذاهب الى ان نعطيكم هو فوضوي كما الجحيم ولكنه يعمل وكنت نقدر أي شخص سواء قائلا "نعم هذا هو فوضوي كما الجحيم ولكن هذه هي الطريقة الوحيدة للقيام بذلك" أو يقول "لا تفعل هذا ... ".

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

وعلى أي حال، وكسر هذه القاعده، تحتاج إلى القيام بذلك على مرحلتين.

1) الأول هو للعودة إلى النتائج الواردة [AID]، [أقرب CreationTime] مما يتيح لك أقرب creationtime لكل AID.

2) ثم يمكنك استخدام latestCreationTime لسحب CID تريد.

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

create view LatestCreationTimes
as
select b.AID,
       max(c.CreationTime) LatestCreationTime
from   TableB b,
       TableC c
where  b.CID = c.CID
group by b.AID

ملحوظة، ليس لدينا تؤخذ بعين الاعتبار الحالة في هذه النقطة.

وتحتاج بعد ذلك للانضمام إلى تلك إلى TableA (للحصول على الحالة) وTableB وTableC (للحصول على CID). ما عليك القيام به جميع الروابط واضحة (AID، CID)، وكذلك الانضمام إلى العمود LatestCreationTime في وجهة النظر إلى العمود CreationTime في TableC. لا ننسى أيضا للانضمام العرض على AID خلاف ذلك حيث تم إنشاء اثنين من السجلات في نفس الوقت لسجلات مختلفة وستحصل القضايا.

select A.AID,
       C.CID
from   TableA a,
       TableB b,
       TableC c,
       LatestCreationTimes lct
where  a.AID = b.AID
and    b.CID = c.CID
and    a.AID = lct.AID
and    c.CreationTime = lct.LatestCreationTime
and    a.STATUS = 'OK'

وأنا على يقين من أن يعمل - لقد اختبرت ذلك، أنب البيانات، اختبارها وأنها تتصرف. على الأقل يفعل ما أعتقد أنه من المفترض أن تفعل.

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

لذلك لست بحاجة لجعل افتراض حول أي واحد كنت تفضل. في هذه الحالة أنا أريد أن أقول أنه إذا كان هناك نوعان من الأرقام التعريفية للعملاء التي تتطابق، وكنت قد واحد أعلى إلى حد ما (هو على الأرجح أكثر حتى الآن).

select A.AID,
       max(C.CID) CID
from   TableA a,
       TableB b,
       TableC c,
       LatestCreationTimes lct
where  a.AID = b.AID
and    b.CID = c.CID
and    c.CreationTime = lct.LatestCreationTime
and    a.STATUS = 'OK'
group by A.AID

وهذا، أعتقد أنه ينبغي أن تعمل من أجلك. إذا كنت تريد ذلك كما استعلام واحد بدلا من وجهة النظر ثم:

select A.AID,
       max(C.CID) CID
from   TableA a,
       TableB b,
       TableC c,
       (select b.AID,
               max(c.CreationTime) LatestCreationTime
        from   TableB b,
               TableC c
        where  b.CID = c.CID
        group by b.AID) lct
where  a.AID = b.AID
and    b.CID = c.CID
and    c.CreationTime = lct.LatestCreationTime
and    a.STATUS = 'OK'
group by A.AID

و(لقد جزءا لا يتجزأ من مجرد وجهة نظر في الاستعلام، وإلا فإن مدير المدرسة هو نفسه تماما).

وليس هناك حاجة لفرعي، وتجميع لتحديد آخر وقت إنشاء إدارة البحث الجنائي واضح وصريح:

SELECT a.aid
    ,c.cid
    ,max(c.creationtime) as maxcCreationTime
FROM c INNER JOIN b ON b.cid = c.cid
    INNER JOIN a on a.aid = b.aid
WHERE a.status = 'OK'
GROUP BY a.aid, c.cid

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

SELECT agg.aid, agg.cid
FROM (
    SELECT a.aid
        ,c.cid
        ,max(c.creationtime) as maxcCreationTime
    FROM c INNER JOIN b ON b.cid = c.cid
        INNER JOIN a on a.aid = b.aid
    WHERE a.status = 'OK'
    GROUP BY a.aid, c.cid
) as agg

والترميز في صفحة الويب، يرجى عذر أي أخطاء في بناء الجملة. أيضا، أنا الرجل MSSQL لذلك آمل أن هناك شيئا مختلفا في العالم أوراكل لهذا ..

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

وأنا شيء مفقود؟ ما هو الخطأ في:

وتحرير: حسنا، أرى أنك فعلا تريد ان مجموعة من المساعدات

.
SELECT c.cid FROM b, c,
    (SELECT b.aid as aid, max(c.creationtime) as creationtime
     FROM b, c
     WHERE b.cid = c.cid
       AND b.aid IN (SELECT a.aid FROM a WHERE status = 'OK')
     GROUP BY b.aid) as z
WHERE b.cid = c.cid
  AND z.aid = b.aid
  AND z.creationtime = c.creationtime
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top