我有一个报告,我在其中捕获患者信息,其中一些存储在患者表中,其中一些存储在观察表中。以出生日期为例,如果我计算所有提供 DOB 的记录,我得到的结果明显多于患者总数,因为加入了观察表。如何对每组仅评估一次运行总数?
编辑: http ://sqlfiddle.com/#! 3/27b91/1/0 上的一些示例数据。如果我从该查询中计算生日,我想要 2 作为答案;种族和民族也是如此。
我有一个报告,我在其中捕获患者信息,其中一些存储在患者表中,其中一些存储在观察表中。以出生日期为例,如果我计算所有提供 DOB 的记录,我得到的结果明显多于患者总数,因为加入了观察表。如何对每组仅评估一次运行总数?
编辑: http ://sqlfiddle.com/#! 3/27b91/1/0 上的一些示例数据。如果我从该查询中计算生日,我想要 2 作为答案;种族和民族也是如此。
对于您的具体情况,以下方法可能是正确的方法,也可能不是正确的方法,但它可能是一种有用的技术,可供您使用。
您可以在您的 select 语句中添加一些代码,以帮助您自己回答诸如“下游”之类的问题(通过添加的标准或通过 SSRS)。请参阅您的 SQL Fiddle 的此修改:
select pid, firstName, lastName, dateOfBirth, obsName, obsValue, obsDate,
rowRank, CASE rowRank WHEN 1 THEN 1 ELSE 0 END AS countableRow
from
(
select Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth
, Obs.obsName, Obs.obsValue, Obs.obsDate,
ROW_NUMBER() OVER (PARTITION BY Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth ORDER BY Obs.obsDate) AS rowRank
from Person
join Obs on Person.pId = Obs.pId
) rankedData
该rowRank
字段将创建一个相对于组的排名编号,这可能对您下游有用,也可能没用。该countableRow
字段将是 1 或 0,这样每个组将有一个且只有一行包含 1。这样做SUM(countableRow)
将为您提供数据中适当数量的组。
现在,您可以通过在每组的第一行中转储实际字段值而不是像 1 这样的常量标量来扩展此功能(如果您愿意)。因此,如果您有CASE rowRank WHEN 1 THEN dateOfBirth ELSE NULL END AS countableDOB
,例如,您可以仅使用此数据集获取每个不同生日的总人数。
当然,无论如何,您都可以使用 @Russell 之类的 SQL 方法来完成所有这些事情,因此这与可能与您的情况不匹配的特定下游需求最为相关。
编辑
显然 countableRow 字段对于您想要的查询类型没有一刀切的解决方案。我在另一个 SQL Fiddle中添加了更多PARTITION BY
策略示例:
select pid, firstName, lastName, dateOfBirth, obsName, obsValue, obsDate,
rowRank, CASE rowRank WHEN 1 THEN 1 ELSE 0 END AS countableRow,
valueRank, CASE valueRank WHEN 1 THEN 1 ELSE 0 END AS valueCount,
dobRank, CASE WHEN dobRank = 1 AND dateOfBirth IS NOT NULL THEN 1 ELSE 0 END AS dobCount
from
(
select Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth
, Obs.obsName, Obs.obsValue, Obs.obsDate,
ROW_NUMBER() OVER (PARTITION BY Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth ORDER BY Obs.obsDate) AS rowRank,
ROW_NUMBER() OVER (PARTITION BY Obs.obsName, Obs.obsValue ORDER BY Obs.obsDate) AS valueRank,
ROW_Number() OVER (PARTITION BY Person.dateOfBirth ORDER BY Person.pid) AS dobRank
from Person
join Obs on Person.pId = Obs.pId
) rankedData
以免有人误解我认为这总是合适的,显然不是。这不是使用附加 SQL 查询获得特定答案的更好解决方案。它允许您做的是对足够的信息进行编码,以便在单个结果集中简单地回答消费代码中的此类问题。这就是它可以派上用场的地方。
第二次编辑
由于您想知道如果比赛数据存储在多个地方是否可以做到这一点,答案是肯定的。我已经修改了我以前的 SQL Fiddle 中的代码,现在可以在新的代码中使用:
select pid, firstName, lastName, dateOfBirth, obsName, obsValue, obsDate,
rowRank, CASE rowRank WHEN 1 THEN 1 ELSE 0 END AS countableRow,
valueRank, CASE valueRank WHEN 1 THEN 1 ELSE 0 END AS valueCount,
dobRank, CASE WHEN dobRank = 1 AND dateOfBirth IS NOT NULL THEN 1 ELSE 0 END AS dobCount,
raceRank, CASE WHEN raceRank = 1 AND (race IS NOT NULL OR obsName = 'RACE') THEN 1 ELSE 0 END AS raceCount
from
(
select Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth, Person.[race]
, Obs.obsName, Obs.obsValue, Obs.obsDate,
ROW_NUMBER() OVER (PARTITION BY Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth ORDER BY Obs.obsDate) AS rowRank,
ROW_NUMBER() OVER (PARTITION BY Obs.obsName, Obs.obsValue ORDER BY Obs.obsDate) AS valueRank,
ROW_NUMBER() OVER (PARTITION BY Person.dateOfBirth ORDER BY Person.pid) AS dobRank,
ROW_NUMBER() OVER (PARTITION BY ISNULL(Person.race, CASE Obs.obsName WHEN 'RACE' THEN Obs.obsValue ELSE NULL END) ORDER BY Person.pid) AS raceRank
from Person
left join Obs on Person.pId = Obs.pId
) rankedData
如您所见,在新的 Fiddle 中,这正确地将 Races 的数量计为 3,其中 2 在 Obs 表中,第三个在 Person 表中。诀窍是PARTITION BY
可以包含表达式,而不仅仅是原始列输出。请注意,我在这里将连接更改为左连接,并且我们需要使用 CASE 来仅包含 obsValue WHERE obsName is 'RACE'。它有点复杂,但不是非常复杂,它甚至可以优雅地处理相当复杂的情况。
事实证明,Jeroen 指向 RunningValue 的指针比我想象的更准确。我能够使用以下代码获得我想要的结果:
=RunningValue(Iif(Not IsNothing(Fields!DATEOFBIRTH.Value)
, Fields!PATIENTID.Value
, Nothing)
, CountDistinct
, Nothing
)
特别感谢 Dominic P,下次我会记住他的技术。
除非他们报告了不同的 DOB,否则这只会为每位患者提取一条记录:
SELECT P.FOO,
P.BAR,
(etc.),
O.DOB
FROM Patients P
INNER JOIN Observations O
ON P.PatientID = O.PatientID
GROUP BY P.FOO, P.BAR, (P.etc), O.DOB