SQL编程了

对应老买买提的军事天地,观点交锋比较激烈。因为此版帖子太多,所以新帖不出现在首页新帖列表,防止首页新帖刷屏太快。

版主: Softfist

回复
头像
TheMatrix楼主
论坛支柱
论坛支柱
2024年度优秀版主
TheMatrix 的博客
帖子互动: 264
帖子: 13231
注册时间: 2022年 7月 26日 00:35

SQL编程了

帖子 TheMatrix楼主 »

这看起来是个经典问题,用其他语言很容易编,但是我是在SQL里面碰到这个问题,也想挑战一下SQL编程。

有一个table,叫Account吧,它有两个columns:
AccountID int primary key,
AccountSize int

比如
AccountID, AccountSize
1, 120
2, 157
3, 196
4, 529
....

给定一个batch_size比如50000吧,把AccountID按顺序赋予BatchOrder,要求每填满一个batch之后(停在第一个多于batch_size的地方,另一个问题是停在最后一个少于batch_size的地方,这个可能简单一点),就赋予一个新的连续的BatchOrder。

最后输出是这样:
AccountID, AccountSize, BatchOrder
1, 120, 1
2, 157, 1
....
87, 324, 2
88, 426, 2
....
头像
CanGuanGong(专门调戏小粉红)
论坛支柱
论坛支柱
CanGuanGong 的博客
帖子互动: 387
帖子: 8860
注册时间: 2022年 7月 23日 18:05

Re: SQL编程了

帖子 CanGuanGong(专门调戏小粉红) »

想在网上骗人帮着免费写code,鄙视!
======================================================
| 离岸小粉红研究中心首席科学家 +新老买提双料精神领袖兼党组书记 +行为艺术买提第一人 |

|【强国领事二奶】公益鉴定活动发起人兼首席鉴定师(link)|
|【我为党妈买遮羞布】活动主发起人[Link]|
=======================================================
头像
TheMatrix楼主
论坛支柱
论坛支柱
2024年度优秀版主
TheMatrix 的博客
帖子互动: 264
帖子: 13231
注册时间: 2022年 7月 26日 00:35

Re: SQL编程了

帖子 TheMatrix楼主 »

CanGuanGong 写了: 2022年 7月 29日 15:47 想在网上骗人帮着免费写code,鄙视!
问题已经抽象得这么清楚了,编程简直是一个享受。
mass
知名作家
知名作家
帖子互动: 23
帖子: 1231
注册时间: 2022年 7月 28日 02:54

Re: SQL编程了

帖子 mass »

GROUP BY估计能行。
MrAnderson
论坛精英
论坛精英
帖子互动: 217
帖子: 7358
注册时间: 2022年 7月 23日 11:57

Re: SQL编程了

帖子 MrAnderson »

select AccountID, AccountSize, count(AccountSize) from Account group by AccountID, AccountSize order by AccountID, count(AccountSize)

应该可以了吧。
逼将有三种:老逼将,小逼将,装逼将
消灭买办是唯一出路
所谓女性解放,就是人类走向灭亡的开端
哈,狗子急了
pda
小有名气
小有名气
帖子互动: 1
帖子: 33
注册时间: 2022年 7月 22日 17:26

Re: SQL编程了

帖子 pda »

用row_number() / batch size 取整即可
TheMatrix 写了: 2022年 7月 29日 15:44 这看起来是个经典问题,用其他语言很容易编,但是我是在SQL里面碰到这个问题,也想挑战一下SQL编程。

有一个table,叫Account吧,它有两个columns:
AccountID int primary key,
AccountSize int

比如
AccountID, AccountSize
1, 120
2, 157
3, 196
4, 529
....

给定一个batch_size比如50000吧,把AccountID按顺序赋予BatchOrder,要求每填满一个batch之后(停在第一个多于batch_size的地方,另一个问题是停在最后一个少于batch_size的地方,这个可能简单一点),就赋予一个新的连续的BatchOrder。

最后输出是这样:
AccountID, AccountSize, BatchOrder
1, 120, 1
2, 157, 1
....
87, 324, 2
88, 426, 2
....
mass
知名作家
知名作家
帖子互动: 23
帖子: 1231
注册时间: 2022年 7月 28日 02:54

Re: SQL编程了

帖子 mass »

pda 写了: 2022年 7月 29日 18:07 用row_number() / batch size 取整即可
他要的是累加再整除,batch_i = sum(acct 1 to i) / batch size
上次由 mass 在 2022年 7月 29日 18:36 修改。
pda
小有名气
小有名气
帖子互动: 1
帖子: 33
注册时间: 2022年 7月 22日 17:26

Re: SQL编程了

帖子 pda »

那就sum(account size) over (order by account id, account size) as running_total,然后running_total/batch size取整
mass 写了: 2022年 7月 29日 18:14 他要的是累加再取模,batch_i = sum(acct 1 to i) / batch size
头像
TheMatrix楼主
论坛支柱
论坛支柱
2024年度优秀版主
TheMatrix 的博客
帖子互动: 264
帖子: 13231
注册时间: 2022年 7月 26日 00:35

Re: SQL编程了

帖子 TheMatrix楼主 »

pda 写了: 2022年 7月 29日 18:34 那就sum(account size) over (order by account id, account size) as running_total,然后running_total/batch size取整
我就是这么做的。这是近似做法。实际问题中这就差不多了。但是要想挑战编程的话,这个是不正确的。
头像
TheMatrix楼主
论坛支柱
论坛支柱
2024年度优秀版主
TheMatrix 的博客
帖子互动: 264
帖子: 13231
注册时间: 2022年 7月 26日 00:35

Re: SQL编程了

帖子 TheMatrix楼主 »

这个应该是必须要用recursive CTE的。我下面这个SQL还不对,原因是analytic function在recursive CTE中不能正常工作。所以这个问题目前还是无解。

with
account_batch as (
select AccountID, AccountSize
, 1 iteration
, case when sum(AccountSize) over (order by AccountID) < 50000
then 1 else 0 end BatchOrder
from Account

union all

select AccountID, AccountSize
, iteration+1 iteration
, case when sum(AccountSize) over (order by AccountID) < 50000
then iteration+1 else 0 end BatchOrder
from account_batch ab
where BatchOrder = 0
)

select AccountID, AccountSize, BatchOrder
from account_batch
where BatchOrder != 0
order by AccountID
头像
zeami(狼VP狈¿为奸)
论坛精英
论坛精英
2023年度十大优秀网友
帖子互动: 690
帖子: 6202
注册时间: 2022年 7月 25日 15:51

Re: SQL编程了

帖子 zeami(狼VP狈¿为奸) »

用awk应该不需要超过十行吧
TheMatrix 写了: 2022年 7月 30日 09:55 这个应该是必须要用recursive CTE的。我下面这个SQL还不对,原因是analytic function在recursive CTE中不能正常工作。所以这个问题目前还是无解。

⚪︎ 辟邪剑法七折大酬宾~~
tmbb2010
见习点评
见习点评
帖子互动: 27
帖子: 1684
注册时间: 2022年 7月 27日 04:32

Re: SQL编程了

帖子 tmbb2010 »

你这个好像要用到比如pl-sql才能解决。也就是程序化的sql语言,再怎么复杂化都可以使用。
头像
TheMatrix楼主
论坛支柱
论坛支柱
2024年度优秀版主
TheMatrix 的博客
帖子互动: 264
帖子: 13231
注册时间: 2022年 7月 26日 00:35

Re: SQL编程了

帖子 TheMatrix楼主 »

zeami 写了: 2022年 7月 30日 10:00 用awk应该不需要超过十行吧
我发现编程就是在限定工具条件下解决问题,当然要已知算法,未知算法的是另外一个问题。

SQL属于工具条件比较简陋,所以用SQL编是一个挑战。

这个问题用函数式编程也不是特别的容易,因为函数式编程的限定条件也比较多。

这个问题本质上就是procedural编程,不用procedural就要发明各种特殊工具。

比如函数式编程里的reduce,还有SQL中的analytic function。
头像
TheMatrix楼主
论坛支柱
论坛支柱
2024年度优秀版主
TheMatrix 的博客
帖子互动: 264
帖子: 13231
注册时间: 2022年 7月 26日 00:35

Re: SQL编程了

帖子 TheMatrix楼主 »

tmbb2010 写了: 2022年 7月 30日 10:06 你这个好像要用到比如pl-sql才能解决。也就是程序化的sql语言,再怎么复杂化都可以使用。
是。但那就不算挑战了。
webbew
见习写手
见习写手
帖子互动: 4
帖子: 81
注册时间: 2022年 7月 26日 17:58

Re: SQL编程了

帖子 webbew »

代码: 全选

with acct as (
    select 1 as AccountId, 34 as AccountSize
    union
    select 2 as AccountId, 6 as AccountSize
    union
    select 3 as AccountId, 7 as AccountSize
    union
    select 4 as AccountId, 60 as Accountsize
    union
    select 5 as AccountId, 4 as AccountSize
    union
    select 6 as AccountId, 23 as AccountSize
    union
    select 7 as AccountId, 33 as AccountSize
    union
    select 8 as AccountId, 10 as AccountSize
    union
    select 9 as AccountId, 14 as AccountSize
    union
    select 10 as AccountId, 30 as AccountSize
    union
    select 11 as AccountId, 8 as AccountSize
    union
    select 12 as AccountId, 17 as AccountSize
),
acct_join as (
    select a.AccountId as IdStart,b.AccountId as IdEnd
    from acct a, acct b
    where a.AccountId<=b.AccountId
),
acct_sum as (
select IdStart, IdEnd,(
        select Sum(AccountSize)
        from acct where Account Id>=IdStart and AccountId<=IdEnd
    ) as SumSize
from acct_join
),
acct_sum_no_larger as (
    select *
    from acct_sum a
    where not exists (
        select *
        from acct_sumb
        where a.IdStart=b.IdStart and b.SumSize<a.SumSize and b.SumSize>=50
    )
),
acct_sum_no_smaller as (
    select *
    from acct_sum_no_larger a
    where not exists (
        select *
        from acct_sum_no_larger b
        where a.IdStart=b.Idstart and b.SumSize>a.SumSize
    )
),
acct_batch as (
    select IdStart, IdEnd, 1 as BatchNum
    from acct_sum_no_smaller
    where IdStart=1
    union all
    select b.IdStart, b.IdEnd, BatchNum+1 as BatchNum
    from acct batch a join acct_sum_no_smaller b on a.IdEnd+1=b.IdStart
)
select * from acct_batch
pda
小有名气
小有名气
帖子互动: 1
帖子: 33
注册时间: 2022年 7月 22日 17:26

Re: SQL编程了

帖子 pda »

recursive cte是正解

declare @batch_size int = 10;

with act as

(

select AccountId, AccountSize, convert(int, row_number() over (order by AccountId, AccountSize)) as rn, 0 as run_total

from account

)

,

cte as

(

select 0 as AccountId, 0 as AccountSize, 0 as rn, 0 as run_total, 0 as batch

union all

select act.AccountId, act.AccountSize, act.rn,

case when cte.run_total + act.AccountSize > @batch_size then act.AccountSize else cte.run_total + act.AccountSize end as run_total,

case when cte.run_total + act.AccountSize > @batch_size then batch + 1 else batch end as batch

from act join cte on cte.rn = act.rn - 1

)

select * from cte

where

accountid > 0





AccountId AccountSize

1 2

2 4

1 3

3 6

5 8

4 2

7 10

9 7



AccountId AccountSize rn run_total batch

1 2 1 2 0

1 3 2 5 0

2 4 3 9 0

3 6 4 6 1

4 2 5 8 1

5 8 6 8 2

7 10 7 10 3

9 7 8 7 4





TheMatrix 写了: 2022年 7月 30日 09:55 这个应该是必须要用recursive CTE的。我下面这个SQL还不对,原因是analytic function在recursive CTE中不能正常工作。所以这个问题目前还是无解。

with
account_batch as (
select AccountID, AccountSize
, 1 iteration
, case when sum(AccountSize) over (order by AccountID) < 50000
then 1 else 0 end BatchOrder
from Account

union all

select AccountID, AccountSize
, iteration+1 iteration
, case when sum(AccountSize) over (order by AccountID) < 50000
then iteration+1 else 0 end BatchOrder
from account_batch ab
where BatchOrder = 0
)

select AccountID, AccountSize, BatchOrder
from account_batch
where BatchOrder != 0
order by AccountID
头像
TheMatrix楼主
论坛支柱
论坛支柱
2024年度优秀版主
TheMatrix 的博客
帖子互动: 264
帖子: 13231
注册时间: 2022年 7月 26日 00:35

Re: SQL编程了

帖子 TheMatrix楼主 »

漂亮。连analytic function都没用。纯classical join。当然,recursive CTE还是必须得用的。
webbew 写了: 2022年 7月 30日 11:52

代码: 全选

with acct as (
    select 1 as AccountId, 34 as AccountSize
    union
    select 2 as AccountId, 6 as AccountSize
    union
    select 3 as AccountId, 7 as AccountSize
    union
    select 4 as AccountId, 60 as Accountsize
    union
    select 5 as AccountId, 4 as AccountSize
    union
    select 6 as AccountId, 23 as AccountSize
    union
    select 7 as AccountId, 33 as AccountSize
    union
    select 8 as AccountId, 10 as AccountSize
    union
    select 9 as AccountId, 14 as AccountSize
    union
    select 10 as AccountId, 30 as AccountSize
    union
    select 11 as AccountId, 8 as AccountSize
    union
    select 12 as AccountId, 17 as AccountSize
),
acct_join as (
    select a.AccountId as IdStart,b.AccountId as IdEnd
    from acct a, acct b
    where a.AccountId<=b.AccountId
),
acct_sum as (
select IdStart, IdEnd,(
        select Sum(AccountSize)
        from acct where Account Id>=IdStart and AccountId<=IdEnd
    ) as SumSize
from acct_join
),
acct_sum_no_larger as (
    select *
    from acct_sum a
    where not exists (
        select *
        from acct_sumb
        where a.IdStart=b.IdStart and b.SumSize<a.SumSize and b.SumSize>=50
    )
),
acct_sum_no_smaller as (
    select *
    from acct_sum_no_larger a
    where not exists (
        select *
        from acct_sum_no_larger b
        where a.IdStart=b.Idstart and b.SumSize>a.SumSize
    )
),
acct_batch as (
    select IdStart, IdEnd, 1 as BatchNum
    from acct_sum_no_smaller
    where IdStart=1
    union all
    select b.IdStart, b.IdEnd, BatchNum+1 as BatchNum
    from acct batch a join acct_sum_no_smaller b on a.IdEnd+1=b.IdStart
)
select * from acct_batch
头像
TheMatrix楼主
论坛支柱
论坛支柱
2024年度优秀版主
TheMatrix 的博客
帖子互动: 264
帖子: 13231
注册时间: 2022年 7月 26日 00:35

Re: SQL编程了

帖子 TheMatrix楼主 »

漂亮。你这个相当于把任意循环用recursive CTE写出来了。SQL就图灵完备了。直接一行一行处理,什么都能干。
pda 写了: 2022年 7月 30日 14:06 recursive cte是正解

declare @batch_size int = 10;
头像
TheMatrix楼主
论坛支柱
论坛支柱
2024年度优秀版主
TheMatrix 的博客
帖子互动: 264
帖子: 13231
注册时间: 2022年 7月 26日 00:35

Re: SQL编程了

帖子 TheMatrix楼主 »

webbew和pda的两种解法是本问题的两个经典解法。

webbew的解法纯用join,把解空间全部列出来,逐步变换缩小。这是SQL表处理的精髓。

pda的解法用SQL recursive CTE实现table逐行处理,也就是实现了函数式编程中的reduce,完成了SQL的图灵完备。
回复

回到 “军事天地(Military)”