【问题标题】:SQL Server - Only get a single row for each distinct value of a fieldSQL Server - 只为字段的每个不同值获取一行
【发布时间】:2020-11-06 05:14:52
【问题描述】:

我刚从面试回家,他们让我参加编程测试。真正困扰我的问题之一如下:

您是一所高中的老师,负责为即将举行的全国辩论锦标赛挑选最好的辩论队。给定以下表结构:

CREATE TABLE CompetitionResults (
    StudentName NVARCHAR(255) NOT NULL,     -- The student's name
    SchoolYear INT NOT NULL,                -- The school year of the student at the time they entered the competition
    CompetitionDate DATE NOT NULL,          -- The date of the competition
    CompetitionResult INT NOT NULL          -- The student's final score in the competition (0 - 100)
)

编写一个查询,根据他们之前的比赛结果返回即将举行的比赛的最佳候选人的姓名。

约束:

  • 返回单列,StudentName
  • 每个学年 (7 - 12 岁) 只能选择一名学生。
  • 每位返校学生今年必须参加过 3 次其​​他比赛。

这是我遇到最多问题的最后一个约束。这是我在时间用完后最终提交的内容:

SELECT
    StudentName AS sn,
    (SELECT COUNT(*) AS NumComps, CompetitionDate FROM CompetitionResults
        WHERE YEAR(CompetitionDate) = 2020 AND NumComps = 3),
    SchoolYear,
    CompetitionDate,
    CompetitionResult
FROM CompetitionResults
WHERE CompetitionDate IN (SELECT MIN(CompetitionDate)
    FROM CompetitionResults GROUP BY CompetitionDate) AND
    CompetitionResult IN (SELECT MAX(CompetitionResult) FROM
    CompetitionResults WHERE StudentName = sn);

为了职业发展,我希望能够在尽可能少的帮助下解决这个问题,但正如您可能知道的那样,我在这里真的很挣扎。这段代码甚至无法编译,更不用说所有子查询的性能影响了!但是,我发现它们比连接更容易编码,因此我在这里使用它们。

非常感谢任何指导/提示。 MTIA :-)

【问题讨论】:

  • Count()HAVING 有什么问题? GROUP BY 也会有所帮助。当您的 SELECT 语句包含 5 列时,您也不可能返回单列。
  • "当您的 SELECT 语句包含 5 列时,您也不可能返回单列"。我很清楚这一点,但我认为这是挑战的一部分。我想需要某种连接或某些东西来删除额外的列......或者我应该制作一个包含所有信息的临时表,并从中选择。我真的不确定:-/
  • @KenWhite,不仅非常有可能,而且非常简单:嵌套选择外部返回您感兴趣的唯一列的位置,对吧?
  • @FDavidov:我指的是 OP 在问题中提供的 SELECT。我没有说其他方式是不可能的。
  • @KenWhite,感谢您的澄清。不过,您不妨考虑改写您的评论。干杯。

标签: sql sql-server distinct


【解决方案1】:

对我来说,这基本上是聚合。 . .带有一点窗口函数:

select studentname, SchoolYear, avg_competitionscore
from (select studentname, SchoolYear, avg(competitionscore) as avg_competitionscore,
             row_number() over (partition by SchoolYear order by avg(competitionscore) desc) as seqnum
      from CompetitionResults cr
      where year(CompetitionDate) = year(getdate())
      group by studentname
      having count(*) = 3
     ) s
where seqnum = 1;

子查询汇总每个学生的比赛,应用适当的过滤条件——包括个人比赛和总人数。外部查询每年选择一个。

我看不出三场比赛与最好的比赛有什么关系。我怀疑根据分数选择最佳学生的部分是一个“隐藏要求”,用于区分仅可接受的解决方案和最佳解决方案。

我想可能有额外的逻辑来检查每年至少有一个候选人,但问题表明至少有一个这样的学生。

【讨论】:

  • 太好了!以前从未遇到过分区,但在阅读了一些关于它们的信息之后,它们似乎是有道理的。谢谢! +1
【解决方案2】:

我想这可以通过窗口函数来解决。举个例子 - 毕竟可能需要一些调整,但你应该明白:

DECLARE @t TABLE(
  StudentName NVARCHAR(255)
 ,SchoolYear INT
 ,CompetitionDate DATE
 ,CompetitionResult INT
)

INSERT INTO @t VALUES
('Peter', 7, '2019-01-01', 100)
,('Peter', 8, '2020-01-01', 100)
,('Peter', 8, '2020-03-01', 100)
,('Paul', 10, '2020-01-01', 100)
,('Paul', 10, '2020-03-01', 100)
,('Paul', 10, '2020-04-01', 100)
,('Mary', 11, '2019-01-01', 100)
,('Mary', 11, '2019-02-01', 100)
,('Mary', 11, '2019-03-01', 100)
,('Jacob', 12, '2020-01-01', 100)
,('Jacob', 12, '2020-02-01', 100)
,('Jacob', 12, '2020-03-01', 100)
,('Jacob', 12, '2020-04-01', 90)
,('Jennifer', 9, '2020-03-01', 100)
,('Jennifer', 9, '2020-04-01', 100)
,('Jennifer', 9, '2020-05-01', 100)
,('Lucas', 12, '2020-03-01', 100)
,('Lucas', 12, '2020-04-01', 100)
,('Lucas', 12, '2020-05-01', 100)

;WITH cte AS(
SELECT *
      ,COUNT(CASE WHEN YEAR(CompetitionDate) = YEAR(GETDATE()) THEN 1 ELSE NULL END) OVER (PARTITION BY StudentName, YEAR(CompetitionDate)) AS CountCompYear
      ,ROW_NUMBER() OVER (PARTITION BY StudentName ORDER BY CompetitionDate DESC) AS LastCompetition
      
  FROM @t
),
cteFilter AS(
SELECT *, ROW_NUMBER() OVER (PARTITION BY SchoolYear ORDER BY CompetitionResult DESC, StudentName ASC) AS DistStudent
  FROM cte
  WHERE CountCompYear = 3
    AND LastCompetition = 1
)
SELECT *
  FROM cteFilter
  WHERE DistStudent = 1

【讨论】:

  • Tyron,我现在无法访问数据库,所以我只是发表评论,没有对您的建议进行任何测试。我认为您缺少一个过滤器:在您的示例中,由于他们参加的比赛数量,每个学年只有一个候选人(即“7”无,“8”无,“9”詹妮弗, “10”保罗,“11”玛丽,“12”雅各布)。如果同一年有两个匹配的人,一个得分为 100、100、100 而另一个得分为 100、100、99,会发生什么?也许,您应该添加 AVERAGE 计算以正确比较,从而能够选择最佳。
  • 还有一点,查询还应该考虑两个学生的平均值完全相同的情况(可能在某处包括TOP 1)。
  • @FDavidov 关于你的第一个问题:Jacob 和 Lucas 都在 12 年级。关于 TOP 1:这是由 cteFilter 中的 ROW_NUMBER 完成的。最后但并非最不重要的一点是:是的,平均值可能是一个解决方案。但是您说“尽可能少的帮助”,因此请将此查询视为草稿,如我的回答中所述,需要进行一些调整。 ;)
  • 泰伦,感谢您的回复。现在,对于第 12 年,我在之前的帖子中犯了一个错误;返回的一个是卢卡斯,因为雅各布被取消资格(4 次参与而不是 3 次),根据ROW_NUMBER,如果您与ORDER BYTOP 1 一起使用,我在您的示例中没有看到的元素确实会有所帮助.最后,您是对的,您的建议是 seed 并且 OP 应该意识到这一点;我的cmets也给他考虑。干杯。
  • OOOPPSSS!!!我错过了这两个(ORDER BY... = 1).. 现在看来没问题。问候!
猜你喜欢
  • 2016-09-29
  • 1970-01-01
  • 1970-01-01
  • 2020-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
相关资源
最近更新 更多