1

長い質問で申し訳ありませんが、あまり説明的なタイトルではありませんが、私の問題を簡単に説明するのは非常に困難です。

3 つのデータベース テーブルがあります。

TABLE A:  
AID PK  
STATUS VARCHAR

TABLE B:  
BID PK  
AID FK  
CID FK

TABLE C:  
CID PK  
CREATIONTIME DATE

テーブル AI の STATUS = 'OK' 行ごとに、最新の作成時刻を持つ C の対応する行を検索します。

最初に、STATUS = 'OK' であるテーブル 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 よりも新しいとします。これは、正しい結果がテーブル C の行 1、2、4、および 6 であることを意味します。

これをクエリで表現する方法はありますか?

編集: 申し訳ありませんが、具体的ではありませんでした。取得したいのは、テーブル C の CID です。

編集:さまざまなソリューションで返された行を数えました。結果は非常に興味深く、多様化しました:
HAINSTECH: 298,473 行
JMUCCHIELLO: 298,473 行
RUSS CAM: 290,121 行
CHRIS: 344,093 行
TYRANNOSAURS: 290,119 行

返された行を詳細に分析する時間はまだありませんが、どのクエリが「壊れている」のか、そしてその理由について意見をいただければ幸いです。

4

6 に答える 6

4

このような何か、私があなたを正しく理解していれば

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を返します。関連するレコードの はidです。CCREATIONTIMESTATUSA'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

次のT-SQLでデモンストレーション

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

返された結果セットが期待どおりのものであるかどうかを確認できるように、与えられた各回答を少数のレコードに対して実行することをお勧めします。

于 2009-04-30T21:06:29.670 に答える
1
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.
于 2009-05-01T07:29:44.977 に答える
1

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);
于 2009-05-02T18:04:12.337 に答える
1

編集:私の以前の答えはナンセンスでした。これで完全に書き直しました

これは実際、私の SQL 人生を通して私を悩ませてきた問題です。私があなたに与える解決策はめちゃくちゃ面倒ですが、うまくいきます。「はい、これはめちゃくちゃ面倒ですが、それを行う唯一の方法です」または「いいえ、これを行う... "。

不安は2つのデートに参加することから来ていると思います. ここで発生する方法は、完全に一致するため問題ではありませんが(ルートデータがまったく同じです)、それでも間違っているように感じます...

とにかく、これを分解すると、これを 2 段階で行う必要があります。

1) 1 つ目は、結果セット [AID]、[earliest 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 列を TableC の CreationTime 列に結合する必要もあります。また、AID のビューに参加することも忘れないでください。そうしないと、異なる A レコードに対して 2 つのレコードが同時に作成され、問題が発生します。

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'

私はそれがうまくいくと確信しています-私はそれをテストし、データを微調整し、再テストしましたが、動作します. 少なくとも、それが意図されていると私が信じていることは実行します。

ただし、テーブル C で同じレコードに対して 2 つの同一の CreationTimes が存在する可能性は扱いません。ただし、説明する必要があることを絶対に制約することをいつか書いていない限り、これは起こらないはずだと思います。

これを行うには、どちらを好むかを推測する必要があります。この場合、一致する CID が 2 つある場合は、より高い方の CID を使用することをお勧めします (最新である可能性が最も高い)。

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

そして、それはあなたのために働くべきだと私は信じています. ビューではなく 1 つのクエリとして使用する場合は、次のようにします。

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

(ビューをクエリに埋め込んだところです。それ以外の場合、プリンシパルはまったく同じです)。

于 2009-04-30T21:17:56.757 に答える
0

私は何かが足りないのですか?何が問題になっていますか:

編集:さて、あなたは実際に援助によってグループ化したいと思っているようです。

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
于 2009-05-02T18:22:57.113 に答える
0

サブクエリは必要ありません。最新の cid 作成時刻を特定するための集計は簡単です。

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

行セットに作成時間が本当に必要ない場合は、それをサブクエリでラップして、プロジェクションから削除できます。

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 派なので、これに関して Oracle の世界でも何も変わらないことを願っています..

提供したスキーマは、cid ごとに CREATIONTIME の一意性を強制しないことに注意してください。同じ作成時間で特定の援助値にマップする cid 値が 2 つある場合、それらは両方とも出力されます。cid,creationtime のペアが一意であることに依存している場合は、制約を使用して宣言的に強制する必要があります。

于 2009-05-02T19:01:45.540 に答える