SQL查询 - 联接子查询

时间:2011-06-28 19:19:08

标签: sql oracle

简介

我需要编写一个复杂的查询(对于Oracle DB)。我试图通过类比来避免使用公司数据,所以如果有些事情看起来很奇怪,请告诉我。

CAT

  • CAT_ID(PK)

小猫

  • KITTEN_ID(PK)

CAT_KITTEN_PLAY

  • PLAY_ID(PK)
  • CAT_ID(FK到CAT表)
  • KITTEN_ID(FK到KITTEN表)
  • KITTEN_GENDER(字符串 - M或F)
  • PLAY_TIME(日期)

KITTEN_INFO

  • KITTEN_ID(FK到KITTEN表)
  • NAME(字符串,例如“姓名”)
  • VALUE(String,例如“Duchess”)

描述

基本上,CAT和KITTEN表只保存ID和其他一些随机信息。 KITTEN_INFO保存小猫的名字(以及其他内容)。 CAT_KITTEN_PLAY会记录猫与小猫玩耍的所有时间。

我需要的是编写一个查询,返回所有与男性KITTEN最近播放时间在某个日期范围内的CAT。我们称之为2011年1月1日至2011年1月31日。我还需要包括猫玩的最近的雄性和雌性小猫的游戏日期和名称。

到目前为止

以下是我到目前为止:

SELECT cat.*, lastMale.PLAY_TIME maleTime, lastFemale.PLAY_TIME femaleTime, maleName.VALUE male, femaleName.VALUE female
FROM CAT cat
LEFT JOIN CAT_KITTEN_PLAY lastMale ON lastMale.CAT_ID = cat.CAT_ID 
                                  AND lastMale.GENDER = 'M'
LEFT JOIN CAT_KITTEN_PLAY lastFemale ON lastFemale.CAT_ID = cat.CAT_ID 
                                    AND lastFemale.GENDER = 'F'
LEFT JOIN KITTEN_INFO femaleName ON femaleName.KITTEN_ID = lastFemale.KITTEN_ID 
                                AND maleName.NAME = 'Name'
LEFT JOIN KITTEN_INFO maleName ON maleName.KITTEN_ID = lastMale.KITTEN_ID 
                              AND femaleName.NAME = 'Name'
    WHERE lastMale.PLAY_TIME BETWEEN '01-JAN-2011 12:00:00 AM'
                                 AND '31-JAN-2011 11:59:59 PM'

问题

这并不能说明一只猫可能不止一次与一只雌性/雌性小猫玩过。所以,我想在第一个连接上添加“AND lastMale.PLAY_TIME =(SELECT MAX(PLAY_TIME)FROM CAT_KITTEN_PLAY WHERE CAT_ID = cat.ID AND KITTEN_GENDER ='M')”到第二个连接。但是连接上不允许子查询。

有什么想法?请注意,猫可能从未玩过一只雌性小猫。但如果它们符合标准(因此左边连接),猫仍然应该被包括在内。

3 个答案:

答案 0 :(得分:3)

分析函数可能是解决问题的最佳方法:

SELECT DISTINCT
       cat.*, 
       first_value(lastMale.PLAY_TIME) 
          over (partition by cat.id 
                order by lastMale.PLAY_TIME desc nulls last) maleTime,
       first_value(lastFemale.PLAY_TIME) 
          over (partition by cat.id 
                order by lastFemale.PLAY_TIME desc nulls last) femaleTime,
       first_value(maleName.VALUE)  
          over (partition by cat.id 
                order by lastMale.PLAY_TIME desc nulls last) male,
       first_value(femaleName.VALUE)  
          over (partition by cat.id 
                order by lastFemale.PLAY_TIME desc nulls last) female
...

答案 1 :(得分:1)

上次比赛的时间,而不是小猫的名字:

SELECT cat.CAT_ID
     , MAX(play.PLAY_TIME) AS maleTime
     , ( SELECT MAX(playf.PLAY_TIME)
         FROM CAT_KITTEN_PLAY AS playf
         WHERE playf.CAT_ID = cat.CAT_ID
           AND playf.KITTEN_GENDER = 'F'
       ) AS femaleTime
FROM CAT AS cat
  JOIN CAT_KITTEN_PLAY AS play
    ON play.CAT_ID = cat.CAT_ID
WHERE play.KITTEN_GENDER = 'M'
GROUP BY cat.CAT_ID 
HAVING MAX(play.PLAY_TIME) BETWEEN '01-JAN-2011 12:00:00 AM'
                               AND '31-JAN-2011 11:59:59 PM'

完全不确定这是否有效:

SELECT cat.CAT_ID
     , lastplayM.PLAY_TIME AS maleTime
     , lastplayF.PLAY_TIME AS femaleTime
     , kittenM.VALUE AS male
     , kittenF.VALUE AS female
FROM CAT AS cat
  JOIN 
    ( SELECT CAT_ID
           , KITTEN_GENDER
           , KITTEN_ID
           , MAX(PLAY_TIME) OVER(PARTITION BY CAT_ID, KITTEN_GENDER) AS PLAY_TIME
      FROM CAT_KITTEN_PLAY
      WHERE PLAY_TIME = MAX(PLAY_TIME) OVER(PARTITION BY CAT_ID, KITTEN_GENDER)
    ) AS lastplayM
    ON lastplayM.CAT_ID = cat.CAT_ID
    AND lastplayM.KITTEN_GENDER = 'M'
    AND lastplayM.PLAY_TIME BETWEEN '01-JAN-2011 12:00:00 AM'
                               AND '31-JAN-2011 11:59:59 PM'
  JOIN KITTEN_INFO AS kittenM
    ON kittenM.KITTEN_ID = lastplayM.KITTEN_ID

  LEFT JOIN 
    ( SELECT CAT_ID
           , KITTEN_GENDER
           , KITTEN_ID
           , MAX(PLAY_TIME) OVER(PARTITION BY CAT_ID, KITTEN_GENDER) AS PLAY_TIME
      FROM CAT_KITTEN_PLAY
      WHERE PLAY_TIME = MAX(PLAY_TIME) OVER(PARTITION BY CAT_ID, KITTEN_GENDER)
    ) AS lastplayF
    ON lastplayF.CAT_ID = cat.CAT_ID
    AND lastplayF.KITTEN_GENDER = 'F'
  JOIN KITTEN_INFO AS kittenF
    ON kittenF.KITTEN_ID = lastplayF.KITTEN_ID

答案 2 :(得分:1)

您可以使用WITH子句使这更容易

WITH MAX_MALE_KITTEN AS
(

     SELECT
        CAT_ID,
        MAX(PLAY_TIME) PLAY_TIME
     FROM
        CAT_KITTEN_PLAY
     WHERE
        KITTEN_GENDER = 'M'
     GROUP BY
        CAT_ID
),
MAX_FEMALE_KITTEN AS
(

     SELECT
        CAT_ID,
        MAX(PLAY_TIME) PLAY_TIME
     FROM
        CAT_KITTEN_PLAY
     WHRE
        KITTEN_GENDER = 'F'
     GROUP BY
        CAT_ID
)

SELECT
    C.CAT_ID,
    F_K_I.NAME LastFemaleName,
    F_PLAY_INFO.PLAY_TIME LastFemalePlayTime,
    M_K_I.NAME LastMaleName,
    M_PLAY_INFO.PLAY_TIME LastMalePlayTime

FROM
    CAT C
    LEFT JOIN CAT_KITTEN_PLAY F_PLAY_INFO
    ON C.CAT_ID = F_PLAY_INFO.CAT_ID
    AND F_PLAY_INFO.KITTEN_GENER= 'F'
    LEFT JOIN MAX_FEMALE_KITTEN M_F_K
    ON F_PLAY_INFO.CAT_ID = M_F_K.CAT_ID
    AND F_PLAY_INFO.PLAY_TIME = M_F_K.PLAY_TIME
    LEFT JOIN KITTEN_INFO F_K_I
    ON F_PLAY_INFO.KITTEN_ID = F_K_I.KITTEN_ID

    INNER JOIN CAT_KITTEN_PLAY M_PLAY_INFO
    ON C.CAT_ID = M_PLAY_INFO.CAT_ID
    AND M_PLAY_INFO.KITTEN_GENER= 'M'
    LEFT JOIN MAX_MALE_KITTEN M_F_K
    ON M_PLAY_INFO.CAT_ID = M_F_K.CAT_ID
    AND M_PLAY_INFO.PLAY_TIME = M_F_K.PLAY_TIME
    LEFT JOIN KITTEN_INFO M_K_I
    ON M_PLAY_INFO.KITTEN_ID = M_K_I.KITTEN_ID
WHERE 
       MAX_MALE_KITTEN.PLAY_TIME    BETWEEN '01-JAN-2011 12:00:00 AM'
                           AND '31-JAN-2011 11:59:59 PM