【问题标题】:SQL: Recursive search for each rowSQL:递归搜索每一行
【发布时间】:2013-07-24 01:56:08
【问题描述】:

我什至不确定这是否可以仅使用 SQL,但这里是...

我在一个表中有一个足球结果列表,每一行都是一场比赛,并包含该比赛的所有数据,即 Home(team)、HomeGoals、AwayGoals、Away(team),我想循环浏览每场比赛,得到主队,查看他们最近 6 场比赛,并仅显示指定球队在最近 6 场比赛中 50% 或更多进球数达到 2 球或以上的比赛。

到目前为止我有这个,我只是不知道如何将它缝合在一起......

创建所有比赛的列表,只返回主队:

SELECT *
FROM [FDATA].[dbo].[Goals]
ORDER BY Date

获取该球队的最后6场比赛:(我想对上述查询的每一行进行以下查询)

SELECT TOP 6 *
FROM [FDATA].[dbo].[Goals]
WHERE Home = '[Home] from first query'  AND Date <= '[Date] from first query'  
ORDER BY Date DESC

然后检查球队在返回的 6 场比赛中是否有 >= 50% 的球进了 2 球或更多球,如果为真则输出第一个查询中的行。

所以基本上对于数据库中的每一行,我想找到该行(比赛)中主队的最后 6 场比赛,看看他们是否在 50% 或更多的比赛中进了 2 球或更多球,如果是,输出原始行,如果没有,则从结果中省略该行。

有可能吗?

【问题讨论】:

  • 您使用的是什么 DBMS? SQL Server、甲骨文、MySQL?它有很大的不同。你的语法看起来像 SQL Server,但我不是 100% 确定。
  • 这绝对是可能的,但它会非常复杂。我希望效率对您来说不是一个大问题。
  • 您的意思是 - 获取在他们参加的 6 场后续比赛中的任何一场比赛中取得 2 个或更多进球且 50% 或更多的球队名单?然后获取这些球队的所有记录,他们是主队?
  • 请问什么版本的 SQL Server?

标签: sql-server tsql sql-server-2012


【解决方案1】:

假设 SQL Server 2012。未经测试,但我认为这应该可行:

WITH Last6 as (
  SELECT 
    Home, HomeGoals, 
    ROW_NUMBER() OVER (PARTITION BY Home ORDER BY Goals.Date DESC) GameNumber
  FROM Goals
),
Medians as (
  SELECT 
    Home, 
    PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY HomeGoals) OVER (PARTITION BY Home) GoalMedian
  FROM Last6
  WHERE GameNumber <= 6
)    
SELECT *
FROM Goals G
WHERE EXISTS (SELECT NULL FROM Medians WHERE G.Home = Medians.Home AND GoalMedian >= 2)

【讨论】:

  • 他希望显示第一个结果集中的每一行,以及每个滚动组 6 场比赛的统计数据。您的查询不符合这些要求。
  • 已编辑:现在更好了,因为我不再需要“DISTINCT Home”了。
  • 我刚刚注意到 OP 想要所有比赛结果,而不仅仅是每个主队的最后 6 个结果,所以我用 EXISTS 稍微更改了查询以访问所有目标表。
  • Jods,此解决方案给出错误:“无法绑定多部分标识符“G.Date”。”奇怪的是列名和表名是正确的。
  • 我的错,我没有将Goals 别名为G。它在Last6 CTE 内,我已将G.Date 更改为Goals.Date。当您不在真实数据库上执行代码时,就会发生这种情况。 ;)
【解决方案2】:
SELECT *, NULL as 50percent2ormore
FROM [FDATA].[dbo].[Goals]
INTO #tempgames
ORDER BY Date

Update #tempgames
SET 50percent2ormore = 'Yes'
FROM ( 
SELECT TOP 6 *, count('games with 2 or more goals') as count
FROM #tempgames
WHERE Home = #tempgames.home  
AND Date <= #tempgames.date
ORDER BY Date DESC 
) tmp
WHERE tmp.count >= 3

SELECT * FROM #tempgames WHERE 50percent2ormore = 'Yes'

这可能是一个开始的地方......

【讨论】:

  • 谢谢,我在尝试运行它时得到了一个完整的语法错误列表。
【解决方案3】:

如果您使用的是 SQL Server 2005 及更高版本:

WITH RankedGoals AS (
   SELECT
      *,
      Sequence = Row_Number() OVER (PARTITION BY G.Home ORDER BY G.Date DESC)
   FROM FDATA.dbo.Goals G
)
SELECT
   G.*
FROM
   RankedGoals G
WHERE
   EXISTS (
      SELECT
         *
      FROM
         RankedGoals S
      WHERE
         G.Home = S.Home
         AND S.Sequence BETWEEN G.Sequence AND G.Sequence + 5
      HAVING
         Sum(CASE WHEN S.Goals >=2 THEN 1 ELSE 0 END) >= 3
         AND Count(*) = 6
   )
;

可以修改查询,以便EXISTS 部分使用TOP 6 来代替日期搜索,这样可能会执行得更好,因为它不必执行窗口功能。但我不确定Date 列是否保证唯一。如果是这样,这很容易:

SELECT
   G.*
FROM
   FDATA.dbo.Goals G
WHERE
   EXISTS (
      SELECT
         *
      FROM
         (
            SELECT TOP 6 *
            FROM
               FDATA.dbo.Goals S
            WHERE
               G.Home = S.Home
               AND S.Date <= G.Date
            ORDER BY
               S.Date
          ) S
      HAVING
         Sum(CASE WHEN S.Goals >=2 THEN 1 ELSE 0 END) >= 3
         AND Count(*) = 6
   )
;

我强烈建议在 HomeDate 列上建立索引...

【讨论】:

  • 感谢您的回复,是的,我使用的是 SQL Server 2012。我尝试了上面的第一个解决方案,但收到错误:“ORDER BY 子句在视图、内联函数、派生表、子查询中无效, 和公用表表达式,除非还指定了 TOP、OFFSET 或 FOR XML。”
  • 第二种解决方案给出了错误:“无法对包含聚合或子查询的表达式执行聚合函数。”
  • 抱歉出现错误。我已经修复了它们......这将让我学会总是做一个有效的例子! :)
  • 干杯,s.goals 仍然有错误,尽管我会使用上面的解决方案,但不要担心,为你的帮助干杯。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-02
  • 2014-01-10
  • 2019-03-12
  • 2016-11-13
相关资源
最近更新 更多