0

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 行のテーブルと合計の集計値を含む別のテーブルがあることに注意してください。したがって、合計の集計に基づいて最初のテーブルに値を挿入する必要があります。

4

3 に答える 3

0

これを試して。これに基づいて独自のテーブルとロジックを使用する必要があります。カーソル付きの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
于 2012-07-21T10:51:49.313 に答える
0

次のように実行できます。このクエリは、最初に教室に最大の定員を設定します。

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
于 2012-07-21T08:44:21.693 に答える
0

満席で先に進むと、1 つまたは複数のクラスを欠席する可能性があります。しかし、その場合、例の場合は 150 に達するまで合計を実行できます。したがって、コール 1 のみが満たされ、クラス 2 の生徒は 0 人になります。これはより簡単な方法です。

他の方法は、合計生徒の比率、つまり 150 と合計定員、つまり 180 を取得し、この比率を使用して各行を乗算して各クラスの生徒を取得することですが、ここでの問題は、一部のクラスでは分数の値を取得する可能性があることです。不可能なので、ラウンドまたは天井を使用する必要がありますが、これらにより、各行の合計分数に応じて、1 人または 2 人の生徒が欠落します。これは扱いにくいものであり、非常に多くの行があると、さらに扱いにくくなる可能性があります。

編集:ここに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 も使用できますが、ネストされたループ結合を使用して各行が数値テーブルと結合されるため、パフォーマンスが低下する可能性があります。

于 2012-07-21T07:21:38.677 に答える