V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  wxf666  ›  全部回复第 28 页 / 共 33 页
回复总数  656
1 ... 20  21  22  23  24  25  26  27  28  29 ... 33  
2022-08-09 01:20:38 +08:00
回复了 shadow1949 创建的主题 程序员 SQL 苦手来请教各位大佬了。
@shadow1949 用不了窗口函数,强行自己模拟,代码看起来会很臭。。


换个思路,如果你能自己维护个『第几个工作日表』,也能很舒服。比如:

  日期  第几个工作日
————— ———————
09-09 周五  1
09-10 中秋 (不要这行)
09-11 周日 (不要这行)
09-12 周一 (不要这行)
09-13 周二  2
09-14 周三  3
09-15 周四  4
09-16 周五  5
09-17 周六 (不要这行)


『 大致 SQL (排版原因,记得去掉每行开头的 全角空格)』

WITH

  workday_data(date, nth, num) AS (
   SELECT date, nth, num
   FROM nth_workday LEFT JOIN orig_data USING(date)
 )

SELECT date, num,
    (SELECT avg(COALESCE(num, 0)) FROM workday_data WHERE nth BETWEEN today.nth - 4 AND today.nth) avg_5,
    (SELECT avg(COALESCE(num, 0)) FROM workday_data WHERE nth BETWEEN today.nth - 9 AND today.nth) avg_10
FROM workday_data today
WHERE num IS NOT NULL;
2022-08-08 23:27:55 +08:00
回复了 huzhikuizainali 创建的主题 C++ 为什么要用递归而不用循环?
@huzhikuizainali 我在想,会不会是你不明白,

同样是“重复”,为何有的用递归,有的用循环?

递归还有一系列入栈出栈操作,既占内存,又耗性能,这货存在意义是什么?!
2022-08-08 23:04:39 +08:00
回复了 huzhikuizainali 创建的主题 C++ 为什么要用递归而不用循环?
@huzhikuizainali 走迷宫用深度优先遍历啊
2022-08-08 23:01:21 +08:00
回复了 huzhikuizainali 创建的主题 C++ 为什么要用递归而不用循环?
@huzhikuizainali 单纯的循环不会进栈

或许,你可以试着,用循环去解决一些,原本用递归干的活儿

比如,写个走迷宫。
再如,用递归下降去解析个 json

可能写多几个,你就不爱用循环+自己维护的栈,去模拟语言实现好的递归了
2022-08-08 22:18:39 +08:00
回复了 simonlu9 创建的主题 程序员 社交权限类似于朋友圈权限,遇到以下场景你会怎样设计
@simonlu9 诶,突然发现,主键设成那样,不就一堆重复的了。。脑子瓦特了

应该是『主键:「作者 ID ,权限类型 TINYINT ,允许用户 ID ,动态 ID 」,时间 』

但叶子节点的结论不变(因为还是 35 字节 / 行)


你说的『 showWith ,user_id 』索引,是『权限类型,作者 ID 』索引的意思吗?
2022-08-08 18:55:37 +08:00
回复了 huzhikuizainali 创建的主题 C++ 为什么要用递归而不用循环?
你不用语言的栈来实现递归,那就用自己的栈来实现循环呗(除非可优化的尾递归)
2022-08-08 18:22:09 +08:00
回复了 simonlu9 创建的主题 程序员 社交权限类似于朋友圈权限,遇到以下场景你会怎样设计
关系数据库新手求问,这样设计,性能会很差吗?

用户动态权限信息表『主键:「作者 ID ,权限类型 TINYINT ,允许用户 ID 」,时间,动态 ID 』

(假设使用 MySQL 的 Innodb 引擎 Dynamic 或 Compact 行格式,则该表的叶节点中,每行记录占 35 字节)


场景一,按时间倒序,获取访问他人主页时,应该能看到的「动态 ID 」列表

select 动态 ID
from 用户动态权限信息表
where 作者 ID = <他人 ID>
  and (权限类型 = <公开> or
   (权限类型 = <指定> and 允许用户 ID = <自己 ID>) or
   (exists <他人和自己是好友> and 权限类型 = <朋友>))
order by 时间 desc;


假设此表 B+ 树有 3 ~ 4 层高,前两层容易被缓存,且都是随机插入,即每个叶节点只有一半可用(能存约 234 行)

如果应能看到他人主页 公开、指定自己、朋友可见『各』 1W 条动态,对于此条查询,我设想会发生的硬盘 IO 次数:


1. 花 1 ~ 2 次 IO ,查询其他表,来确定 <他人和自己是好友>

2. 查询 <公开>、<指定><自己 ID>、<朋友可见> 每种类型的前 234 条记录,各花费 1 ~ 2 次 IO (从树根向叶子查)

往后每查询 234 条,再花费 1 次 IO (叶子是双向链表)。则总计 3 * ( 1~2 + ceil(10000 / 234)) = 132 ~ 135 次 IO


总结:若某人有(除自己可见外)各种类型『共』 3W 条动态,为获得这些动态 ID 列表,需读取硬盘 130 多次

如果固态 16KB IOPS 有 13W ,那每秒最多可查询 1000 个类似这样的人的所有动态 ID 列表

不知算得对不对
2022-08-07 12:07:57 +08:00
回复了 haoliang 创建的主题 Python Python 这个 scope/block leak 我是越来越膈硬
这……是一个函数内揉了太多内容,还是不同含义的变量都用同一个命名表示了?
2022-08-07 10:32:32 +08:00
回复了 shadow1949 创建的主题 程序员 SQL 苦手来请教各位大佬了。
@shadow1949 ,改写成了 MySQL ,将查询放进了一个视图中。

如果用 SQL 的话,以后一行「 select * from view_xxx 」就可得到结果了

使用前,记得将「调休」「非周末的其他假日」添加进『 holiday 表』



『「 select * from view_xxx 」结果』

  date    num   avg_5   avg_10
————— —— ———— ————
2022-09-01   12   12.0000   12.0000
2022-09-20   11   02.2000   01.1000
2022-09-21   05   03.2000   01.6000
2022-09-22   23   07.8000   03.9000
2022-09-23   42   16.2000   08.1000



『 MySQL 语法(排版原因,记得去掉每行开头的 全角空格)』


-- 节假日数据(默认周六周日是假日。若有调休、其他假日,在此表指定)
CREATE TABLE holiday(date DATE PRIMARY KEY, is_holiday BOOL NOT NULL) AS
  SELECT * FROM (
   VALUES
   -- 中秋放假
    ROW('2022-09-12', true),
   -- 国庆放假和调休
    ROW('2022-10-01', true), ROW('2022-10-04', true), ROW('2022-10-07', true),
    ROW('2022-10-02', true), ROW('2022-10-05', true), ROW('2022-10-08', false),
    ROW('2022-10-03', true), ROW('2022-10-06', true), ROW('2022-10-09', false)
 ) AS v(date, is_holiday);

-- 原始数据(日期、数据)
CREATE TABLE orig_data(date DATE PRIMARY KEY, num INT NOT NULL) AS
  SELECT * FROM (
   VALUES
    ROW('2022-09-01', 12), ROW('2022-09-03', 22),
    ROW('2022-09-20', 11), ROW('2022-09-21', 5),
    ROW('2022-09-22', 23), ROW('2022-09-23', 42),
    ROW('2022-09-24', 11)
 ) AS v(date, num);

-- 原始数据的各种平均值视图
CREATE VIEW avgs_of_data AS

  WITH RECURSIVE

  -- 根据原始数据的日期范围,生成日历
  -- (如果超过 1000 天,记得调整 cte_max_recursion_depth )
   calendar(date) AS (
    SELECT min(date)
     FROM orig_data
    UNION ALL
    SELECT DATE_ADD(date, INTERVAL 1 DAY)
     FROM calendar
    WHERE date < (SELECT max(date) FROM orig_data)
  ),
  
  -- 根据日历,生成工作日数据(日期、该天是否为工作日)
   workday(date, is_workday) AS (
    SELECT date, COALESCE(NOT is_holiday, weekday(date) < 5)
    FROM calendar LEFT JOIN holiday USING(date)
  ),

  -- 为每个工作日,计算最近 5 或 10 个工作日内的平均值(日期、数值、5 工作日均值、10 工作日均值)
   avgs_of_workday(date, num, avg_5, avg_10) AS (
    SELECT date, num,
       avg(COALESCE(num, 0)) OVER (ORDER BY date ROWS 4 PRECEDING),
       avg(COALESCE(num, 0)) OVER (ORDER BY date ROWS 9 PRECEDING)
     FROM workday LEFT JOIN orig_data USING(date)
    WHERE is_workday
  )

 -- 去除没有数据的工作日
  SELECT *
   FROM avgs_of_workday
  WHERE num IS NOT NULL;


MySQL 不支持在「窗口函数」中使用「 group_concat 」,所以没有「(11+5+23) / 5 」之类的结果了。

但对你的需求无影响,只是方便查看平均值的计算过程是否正确。
2022-08-06 02:22:48 +08:00
回复了 shadow1949 创建的主题 程序员 SQL 苦手来请教各位大佬了。
@shadow1949 搞出来了,『「一条」普通 SQL 语句』

不用「建表、存储过程、事务」,连 SQLite 都能胜任的,普通语句。

去掉「节假日数据」和「测试数据」后,大概 20 行


『 SQLite 语法(排版原因,记得去掉每行开头的 全角空格),改成 MySQL 应该也很容易』

WITH RECURSIVE

 -- 节假日数据(默认周六周日是假日。若有调休、其他假日,在此表指定)
  holiday(date, is_holiday) AS (
   VALUES
   ('2022-09-12', true),
   ('2022-10-01', true), ('2022-10-04', true), ('2022-10-07', true),
   ('2022-10-02', true), ('2022-10-05', true), ('2022-10-08', false),
   ('2022-10-03', true), ('2022-10-06', true), ('2022-10-09', false)
 ),

 -- 测试数据(日期,数据)
  test_data(date, num) AS (
   VALUES
   ('2022-09-01', 12), ('2022-09-22', 23),
   ('2022-09-03', 22), ('2022-09-23', 42),
   ('2022-09-20', 11), ('2022-09-24', 11),
   ('2022-09-21', 5)
 ),

 -- 九月份日历(此表有 30 行数据)
  calendar(date) AS (
   SELECT '2022-09-01'
   UNION ALL
   SELECT date(date, '+1 day')
    FROM calendar
   WHERE date < '2022-09-30'
 ),

 -- 根据日历,生成工作日数据(日期,该天是否为工作日)
  workday(date, is_workday) AS (
   SELECT date, COALESCE(NOT is_holiday, 0 + strftime('%w', date) BETWEEN 1 AND 5)
    FROM calendar LEFT JOIN holiday USING(date)
 )

-- 1. 根据工作日日历,测试数据中丢弃节假日的行,补充其他缺失工作日的行(这些新行的 num IS NULL )
-- 2. 利用范围为当前行及之前 4 行的窗口函数,按照日期顺序,滑动计算窗口内的平均值
-- 3. 丢弃第 1 步中,补充的行(即 num IS NULL 的行)

SELECT *
  FROM (
  SELECT date, num, avg(COALESCE(num, 0)) OVER win avg,
     format('(%s) / %d', group_concat(num, '+') OVER win, COUNT(*) OVER win) expr
   FROM workday LEFT JOIN test_data USING(date)
  WHERE is_workday
  WINDOW win AS (ORDER BY date ROWS 4 PRECEDING)
)
WHERE num IS NOT NULL;


『输出』

   date    num   avg      expr
————— —— —— —————————
2022-09-01   12   12.0  (12) / 1
2022-09-20   11   02.2  (11) / 5
2022-09-21   05   03.2  (11+5) / 5
2022-09-22   23   07.8  (11+5+23) / 5
2022-09-23   42   16.2  (11+5+23+42) / 5
2022-08-05 23:08:04 +08:00
回复了 shadow1949 创建的主题 程序员 SQL 苦手来请教各位大佬了。
@shadow1949 ,为嘛你第二条附言里的『 query sql 』,结果看着不太对呢?

id     date    num   avg_num
—— ———— ——— ————
01   2022-09-01   12   12.0000
13   2022-09-20   11   2.2000 『不应该是 11 / 1 = 11 么』
14   2022-09-21   05   3.2000 『(11+5) / 2 = 8 ?』
15   2022-09-22   23   7.8000 『(11+5+23) / 3 = 13 ?』
16   2022-09-23   42   16.2000 『(11+5+23+42) / 4 = 20.25 ?』


结果里缺失了『 2022-09-03 』『 2022-09-24 』,看来 24 楼里的问题,你的计划应该是『节假日不出现在结果列表里』

我总觉得可以『「一条」普通 SQL 语句』搞定这个问题。等我试试
2022-08-05 11:07:06 +08:00
回复了 shadow1949 创建的主题 程序员 SQL 苦手来请教各位大佬了。
@shadow1949 ,我还好奇一件事

假如表中就两项:

1. 2022-08-05 (周五) num: 100
2. 2022-08-06 (周六) num: 200

周六前五天,num 的平均值,你计划算出啥结果?

1. null
2. 不出现在结果列表
3. (100) / 1 = 100 (因为跳过周末)
4. (200 + 100) / 2 = 150 (因为包括当天)
2022-08-05 09:43:10 +08:00
回复了 shadow1949 创建的主题 程序员 SQL 苦手来请教各位大佬了。
马也,题目还有补充信息。。还要算节假日?
2022-08-05 09:39:14 +08:00
回复了 shadow1949 创建的主题 程序员 SQL 苦手来请教各位大佬了。
数据库新手试答一下

前面有大佬说了,任意连续 7 天必包含周六周日。只需算过去 7 天非周六周日的平均数即可

『 MySQL 语法(应该是这样吧)』:

WITH
  orig_data(date, num) AS (
   VALUES
    ROW('2022-07-29', 1), ROW('2022-07-23', 5),
    ROW('2022-07-30', 2), ROW('2022-07-21', 11),
    ROW('2022-07-31', 3), ROW('2022-06-11', 22),
    ROW('2022-08-01', 4), ROW('2022-06-10', 12),
    ROW('2022-08-02', 5),
    ROW('2022-08-03', 6),
    ROW('2022-08-04', 7),
    ROW('2022-08-05', 8)
 )

SELECT *,
    avg(CASE WHEN weekday(date) < 5 THEN num ELSE null END)
    OVER (ORDER BY date(date) RANGE INTERVAL 6 DAY PRECEDING) avg
  FROM orig_data


『结果』:

   date    num   avg
————— —— ————
2022-06-10   12   12.0000
2022-06-11   22   12.0000
2022-07-21   11   11.0000
2022-07-23   5   11.0000
2022-07-29   1   1.0000
2022-07-30   2   1.0000
2022-07-31   3   1.0000
2022-08-01   4   2.5000
2022-08-02   5   3.3333
2022-08-03   6   4.0000
2022-08-04   7   4.6000
2022-08-05   8   6.0000
2022-08-05 08:26:16 +08:00
回复了 shadow1949 创建的主题 程序员 SQL 苦手来请教各位大佬了。
『前五天』包括当天吗?比如,2022-07-23 『前五天』是( 23, 22, 21, 20, 19 )还是( 22, 21, 20, 19, 18 )?

『跳过周末,往前顺延』要将周末计算在内吗?比如,是(周一日六五四三二)还是(周一五四三二)?
2022-08-05 04:24:03 +08:00
回复了 UN2758 创建的主题 问与答 SQL 不会写了,求问一个简单 SQL 语句
@UN2758 2 楼 3 楼都是同一个思路『窗口函数』吧。。

都是计算出类似下表后,再取『排名 = 1 』的行:


班级 课程 选课数 排名
—— —— ——— ———
1 班 语文   2     1
1 班 数学   2     1
2 班 语文   2     1
2 班 数学   1     2
3 班 数学   2     1
3 班 语文   1     2
2022-08-04 20:51:19 +08:00
回复了 tangbj 创建的主题 程序员 求教, Excel 存储的题库,怎么快速生成题本和解析
@GodThemselves @tangbj 又搜了搜,大致瞅了瞅,

『 docxtpl 』这个 python 库可以做到类似 18 楼 那样,预先建一个『模板.docx 』,然后填充 3000 题,最后保存成新文件(有点类似 9 楼 说的邮件合并?)

实在要用 Excel 存数据的话,『 pandas 』应该能比较好地满足楼主的需求:读取 Excel 、根据条件筛选


话说 @tangbj ,你的原始数据长啥样的?

不知你是不是被『如何把所有题型塞一张表里』难住了。其实不一定要全部塞一张表里的
@bulay emm ,没有经验,连你的原始需求是啥都猜不出来。。等大神指点了
2022-08-04 17:15:06 +08:00
回复了 tangbj 创建的主题 程序员 求教, Excel 存储的题库,怎么快速生成题本和解析
@GodThemselves 文档生成这块,我也没啥经验

刚搜了搜,python 居然没有多少操控 markdown 的,几乎全是 markdown 转 html 的。。

虽说按楼主要求直接生成 markdown 很容易(还要处理下转义):

for index, pro in enumerate(problems):
  fp.write(f'{index + 1}.({pro.题型}){pro.题目}\n')
  if pro.题图:
   fp.write(f'![]({pro.题图})\n')
  for j, choice in enumerate(filter(None, (pro.A, pro.B, pro.C, pro.D))):
   fp.write(…)

但通用性太差了,专为楼主需求定制,没长进多少经验……

我自己的话,若想答,总想能干的更通用些,答案以后也更可能有用
@bulay Emm…… ssh 动态端口转发?
1 ... 20  21  22  23  24  25  26  27  28  29 ... 33  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2642 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 24ms · UTC 11:08 · PVG 19:08 · LAX 03:08 · JFK 06:08
Developed with CodeLauncher
♥ Do have faith in what you're doing.