1

假设我有下表:

CREATE TABLE #temp(AnimalId varchar(5), BodyPartId varchar(5), Value decimal)
INSERT INTO #temp
SELECT 'A', 'X', 50 union all
SELECT 'A', 'Y', 75 union all
SELECT 'A', 'B', 100 union all
SELECT 'B', 'K', 60 union all
SELECT 'B', 'J', 53

动物是由具有一定价值的身体部位组成的。他们的身体部位也可以是其他动物。

我需要能够用其他动物身体部位的价值替换其他动物的身体部位。

是否可以在 SQL 中执行此操作?

要使用上面的示例进行计算,我会找出哪些身体部位是动物(B)。然后对于这些动物,我会计算出每个身体部位构成的百分比。

K divided by total of B: 60/113 = 0.53
J divided by total of B: 53/113 = 0.47

然后我将这些值乘以 B 在 A 中的总数:

Ks percentage times 100: 0.53 * 100 = 53
Js percentage times 100: 0.47 * 100 = 47

所以动物A的最终组成是:

X 50
Y 75
K 53
J 47

我正在努力寻找每个身体部位相对于动物的百分比。我假设动物只能由仅由身体部位组成的其他动物组成 - 所以我不需要递归解决方案(尽管很高兴看到)。

4

1 回答 1

3

我已经制定了一个似乎可行的解决方案,但最好在更复杂的场景中对其进行测试或了解可能涉及哪些类型的场景,例如子子动物或多个动物的子动物亲代动物,或复制的身体部位等。

首先,计算作为动物的一部分的身体部位的百分比和总数,该动物是亲本动物的子动物,并使用新的总数更新回表格。此外,更新父动物以匹配正确的新父动物:

-- update body parts of sub-animals to new value and parent animal
-- also set parent animal bodypartid to itself so it can be identified
with animalbodyparts as (
  select * from animals
  where bodypartid in (select animalid from animals)
), totals as (
  select a.animalid, sum(a.value) as subtotal
  from animals a
  group by a.animalid
), newtotals as (
  select ab.animalid as parentanimalid, t.animalid,
    p.bodypartid, p.value / t.subtotal as percentage,
    ab.value as newtotal, cast(p.value / t.subtotal * ab.value as integer) as newvalue
  from animalbodyparts ab
  join totals t on ab.bodypartid = t.animalid
  join animals p on t.animalid = p.animalid
)
update a
set
  a.animalid = 
    case
      when t.parentanimalid is null then a.animalid
      else t.parentanimalid
    end,
  a.bodypartid =
    case
      when t.animalid = a.bodypartid then t.parentanimalid
      else a.bodypartid
    end,
  a.value =
    case
      when t.newvalue is null then a.value
      else t.newvalue
    end
from animals a
left join newtotals t on a.bodypartid = t.bodypartid
  or t.animalid = a.bodypartid;

此外,当 bodypartid 是对作为身体部位的子动物的引用时,我将 bodypartid 更新为与父动物相同。这是因为一旦我更新了子部分的动物 ID,就没有其他方法可以引用曾经引用过动物的身体部位。我将它更新为相同的值,这样我就可以知道需要删除哪些,因为它们现在具有匹配的 bodypartid 和 animalid 值:

--cleanup by removing the body parts that were sub-animals
delete from animals where animalid = bodypartid

演示:http ://www.sqlfiddle.com/#!3/6a5a0/39

注意:我得到了 for 的最终结果,46因为J我在总计算之后四舍五入,而您在计算新值之前先四舍五入百分比。如有必要,这应该很容易改变。请注意,无论使用哪种方法,都不能保证新值将与原来的旧总数相加(即 47 + 53 = 100)。有 - 罕见的 - 病理情况(可能涉及子动物的 3 个或更多身体部位),即使您先四舍五入,最终的总数也会与原始情况略有不同。

于 2012-08-23T13:53:43.023 回答