![](/img/trans.png)
[英]SQL: Need to get MAX values from one table and divide by the total number of rows in another table
[英]SQL - I need to divide a total value into multiple rows in another table
可以说我在SQL Server 2008中具有以下表格
学校桌
School_Id |Course_Id | Total Students |
---------------------------------------
1 Acct101 150
1 Acct102 100
2 Acct101 110
2 Acct102 130
类表
School_Id |Course_Id | Class_ID | Capacity
---------------------------------------
1 Acct101 A1 65
1 Acct101 A2 50
1 Acct101 A3 70
1 Acct102 Ab1 100
1 Acct102 Ab2 100
2 Acct101 B1 80
2 Acct101 B2 90
根据给定的信息,我需要显示每个班级的学生总数,如下所示:
School_Id |Course_Id | Class_ID | Capacity | Students
-------------------------------------------------
1 Acct101 A1 65 60
1 Acct101 A2 45 40
1 Acct101 A3 70 50
1 Acct102 Ab1 100 70
1 Acct102 Ab2 100 30
2 Acct101 B1 80 60
2 Acct101 B2 90 50
我基于在学校表的第一行中的学生总价值150获得了前3行中的学生价值
因此,我将150分为3个班级,并在划分时考虑了班级人数。 (我只需要使我的总数在班级限制之内的任何数字组合即可。而且,所有教室必须有学生,因此班级零学生是不可接受的)
我该如何实现?
请注意,我有一个包含+500,000行的表,另一个有汇总值的表,因此我需要根据汇总表的值在第一个表中插入值。
如果您全力以赴,那么您可能会错过一门或多门课程。但是在这种情况下,您可以进行总计学习直到您说出150分为止;但是对于第二堂课,您的总能力为100,而学生总数为100因此只会填充1个电话,而2级将有0个学生。这是更简单的方法。
另一种方法是,您获得总学生的比例,即150,总能力的比例,即180,并使用该比例与每一行相乘,得出每个班级的学生人数,但是这里的问题是,对于某些班级,您可能会获得分数值这是不可能的,因此您必须使用圆形或上限,但这会导致1或2个学生丢失,具体取决于每一行的总分数。 这是一个棘手的问题,并且由于行数太多,可能会更加棘手。
编辑:在这里添加第二种方法。
select cs.*,round(cs.capacity*rt.ratio,0) from
(
select cp.*,sc.totalstudents/cp.cap as ratio
from (select schoolid,courseid,sum(capacity) cap from class ) as cp
inner join
school sc on cp.schoolid = sc.schoolid and sc.courseid = cp.courseid
) rt
inner join class cs
on cs.schoolid = rt.schoolid and rt.courseid = cs.courseid
这应该可以近似地工作,但是可能会错过一些东西或有更多的学生。根据索引,这也应该非常有效。
您也可以使用NTILE,但是这可能会影响性能,因为每一行最有可能使用嵌套循环联接与数字表联接。
您可以这样做,此查询首先填充容量最大的教室:
DECLARE @School TABLE (School_Id INT,Course_Id
VARCHAR(50), Total_Students INT)
DECLARE @Class TABLE (School_Id INT,Course_Id
VARCHAR(50), Class_ID VARCHAR(50), Capacity INT)
INSERT @School VALUES
(1, 'Acct101' ,150),
(1, 'Acct102' ,100),
(2, 'Acct101' ,110),
(2, 'Acct102' ,130)
INSERT @Class VALUES
(1, 'Acct101' ,'A1' ,65),
(1, 'Acct101' ,'A2' ,50),
(1, 'Acct101' ,'A3' ,70),
(1, 'Acct102' ,'Ab1' ,100),
(1, 'Acct102' ,'Ab2' ,100),
(2, 'Acct101' ,'B1' ,80),
(2, 'Acct101' ,'B2' ,90)
;WITH y AS (
SELECT a.*,
ROW_NUMBER() OVER
(PARTITION BY a.School_ID, a.Course_ID ORDER BY a.Capacity DESC)
CapacitiyOrderPerSchoolAndCourse,
SUM(a.Capacity) OVER
(PARTITION BY a.School_ID, a.Course_ID)
TotalCapacityForSchoolAndCourse,
b.Total_Students TotalParticipants
FROM @Class a
JOIN @School b ON
b.School_Id = a.School_Id
AND b.Course_Id = a.Course_Id
), z AS(
SELECT x.School_Id,
x.Course_Id,
y.TotalCapacityForSchoolAndCourse,
y.TotalParticipants,
CASE WHEN y.TotalParticipants < SUM(x.Capacity) THEN
y.TotalParticipants
ELSE
SUM(x.Capacity)
END NumberOfStudentsInClasses,
MIN(y.Capacity) ClassCapacity,
y.Class_ID ClassName,
MIN(y.Capacity) -
CASE WHEN y.TotalParticipants - SUM(x.Capacity) < 0 THEN
ABS(y.TotalParticipants - SUM(x.Capacity))
ELSE
0
END StudentsInClass
FROM y
JOIN y x ON x.School_Id = y.School_Id
AND x.Course_Id = y.Course_Id
AND x.CapacitiyOrderPerSchoolAndCourse
<= y.CapacitiyOrderPerSchoolAndCourse
GROUP BY x.School_Id,
x.Course_Id,
y.CapacitiyOrderPerSchoolAndCourse,
y.Class_ID,
y.TotalCapacityForSchoolAndCourse,
y.TotalParticipants
)
SELECT
z.School_Id,
z.Course_Id,
z.TotalCapacityForSchoolAndCourse,
z.TotalParticipants,
z.ClassName,
z.ClassCapacity,
CASE WHEN StudentsInClass < 0 THEN
0
ELSE
StudentsInClass
END StudentsInClass
FROM z
如果您希望每个教室都有一定数量的学生,则可以这样操作(它会根据教室的容量向每个教室分配一定数量的学生):
;WITH y AS (
SELECT a.*,
SUM(a.Capacity) OVER
(PARTITION BY a.School_ID, a.Course_ID)
AS TotalCapacityForSchoolAndCourse,
b.Total_Students TotalParticipants
FROM @Class a
JOIN @School b ON
b.School_Id = a.School_Id
AND b.Course_Id = a.Course_Id
), z AS(
SELECT y.School_Id,
y.Course_Id,
y.TotalCapacityForSchoolAndCourse,
y.TotalParticipants,
MIN(y.Capacity) ClassCapacity,
y.Class_ID,
MIN(y.Capacity) * 1.0 / y.TotalCapacityForSchoolAndCourse
AS PercentOfCapacity,
ROUND(
MIN(y.Capacity) * 1.0 / y.TotalCapacityForSchoolAndCourse
* TotalParticipants
, 0, 0)
AS NumberOfStudents
FROM y
GROUP BY y.School_Id,
y.Course_Id,
y.Class_ID,
y.TotalCapacityForSchoolAndCourse,
y.TotalParticipants
)
, i AS(
SELECT
z.School_Id,
z.Course_Id,
z.TotalCapacityForSchoolAndCourse,
z.TotalParticipants,
z.Class_ID,
z.ClassCapacity,
PercentOfCapacity,
NumberOfStudents,
SUM(NumberOfStudents) OVER
(PARTITION BY z.School_Id, z.Course_Id)
AS SumNumberOfStudents,
ROW_NUMBER() OVER
(PARTITION BY z.School_Id, z.Course_Id
ORDER BY NumberOfStudents)
AS ClassWithSmallestCapacity
FROM z
), j AS(
SELECT i.School_Id,
i.Course_Id,
i.TotalCapacityForSchoolAndCourse,
i.TotalParticipants,
i.Class_ID,
i.ClassCapacity,
i.PercentOfCapacity,
i.NumberOfStudents,
i.NumberOfStudents +
CASE WHEN ClassWithSmallestCapacity = 1 THEN
TotalParticipants - SumNumberOfStudents
ELSE 0
END AS NumberOfStudents2
FROM i
)
SELECT *
FROM j
尝试这个。 您必须基于此使用自己的表和逻辑。带有游标的Ntile将返回完美的数据,并且也应该安静一些。此方法还需要一个数字表。
set nocount on
go
drop table soh
go
select salesorderid,round(totaldue -10,0)%180 totaldue into soh
from Sales.SalesOrderHeader
go
create unique clustered index idx_soh_id on soh(salesorderid)
go
drop table sod
go
select salesorderid,salesorderdetailid,round(linetotal,0)%200 as linetotal into sod
from sales.SalesOrderDetail
go
create unique clustered index idx_sod_id on sod(salesorderid,salesorderdetailid)
go
drop table #sod
go
declare @salesorderid int,@totaldue float,@cnt int,@sm float
declare cr_cursor cursor fast_forward
for select salesorderid ,totaldue from soh
select salesorderid,salesorderdetailid, linetotal,0 as val,ROW_NUMBER() over(partition by salesorderid order by linetotal asc) as rn
into #sod from sod
create unique clustered index idx_#sod on #sod(salesorderid,salesorderdetailid)
open cr_cursor
while(1=1)
begin
fetch cr_cursor into @salesorderid,@totaldue
if (@@FETCH_STATUS <> 0)
begin
break
end
select @sm =sUM(linetotal) ,@cnt = COUNT(salesorderdetailid)
from #sod where salesorderid = @salesorderid
;with cte as
(select @salesorderid salesorderid,nt,count(*) cnt from
(select NTILE(@cnt) over( order by n.cnt asc) nt from nums n where n.cnt <= @sm - @totaldue ) dta
group by nt
)
update s
set val = c.cnt
from #sod s inner join cte c on s.SalesOrderID= c.salesorderid and s.rn= c.nt
end
close cr_cursor
deallocate cr_cursor
select * from #sod
go
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.