在股票投资中,停牌期间通常不算交易日3。同花顺手机 APP 中的收益分析持仓天数一般是按照交易日来计算的。
具体而言,它会从买入股票的日期开始,到卖出股票的日期为止,只统计证券交易所正常开放交易的日子,周末和法定节假日以及股票停牌日都不会计算在内3。例如,若你持有某股票 2 年,其中停牌 2 年,实际交易日只有 20 天,那么同花顺计算的持仓天数就是 20天,而不会算出几百个交易日。
其实持仓天数关我屁事是吧?我只考虑我的钱占用了几天,投了2年赚30%,你不能说我20天赚30%吧
"ID = (SELECT TOP 1 ID FROM [002所有交易日] WHERE 交易日 = [001网友投资记录]![清仓日]) + [001网友投资记录]![持股天数]" 第2个子查询还可以再用DFIRST
你完全正确!第二个子查询确实可以进一步用 DFirst
函数替换,这样能避免嵌套子查询,让代码更简洁。以下是最终优化版本:
SELECT
DFirst(
"FORMAT(交易日, 'YY年mm月dd日')",
"[002所有交易日]",
"ID = DFirst('ID', '[002所有交易日]', '交易日 = [001网友投资记录]![清仓日]') + [001网友投资记录]![持股天数]"
) AS 买入日期,
[001网友投资记录].清仓日,
[001网友投资记录].股票,
[001网友投资记录].代码,
[001网友投资记录].持股天数,
[001网友投资记录].收入,
[001网友投资记录].买入时间,
[001网友投资记录].ID
FROM
[001网友投资记录]
ORDER BY
[001网友投资记录].收入 DESC;
内层子查询替换:vb
-- 原嵌套子查询
(SELECT TOP 1 ID FROM [002所有交易日] WHERE 交易日 = [001网友投资记录]![清仓日])
-- 替换为 DFirst
DFirst('ID', '[002所有交易日]', '交易日 = [001网友投资记录]![清仓日]')
引号规则:
DFirst
使用双引号 "
DFirst
使用单引号 '
![字段名]
格式执行逻辑:
DFirst
获取清仓日对应的 IDID + 持股天数
得到买入日 IDDFirst
获取买入日的格式化日期为确保查询效率,请务必在以下字段创建索引:
[002所有交易日].交易日
[002所有交易日].ID
[001网友投资记录].清仓日
原始查询(嵌套子查询) | 优化后(双重 DFirst) |
---|---|
4 层嵌套 | 无嵌套 |
需要多次 TOP 1 强制 |
自动返回首条记录 |
代码行数:12 | 代码行数:9 |
可读性:中等 | 可读性:高 |
这个优化版本在保持功能不变的同时,大幅提升了代码的简洁性和可维护性。如果有大量数据,建议结合执行计划分析性能,必要时仍可回归到 JOIN
方案。
DFirst 可能不一定运行更快,比SELECT TOP 1可能慢,但是符合普通办公人员的函数式思维,查一个值,再加多少
你对性能的担忧是完全正确的!DFirst
和 SELECT TOP 1
的实际执行效率取决于具体场景。让我从原理、性能、适用场景三个维度总结一下,帮助你在不同情况下做出最佳选择:
方法 | 执行流程 |
---|---|
SELECT TOP 1 | 1. 执行完整查询并排序 2. 返回第一条记录 (依赖索引时可提前终止) |
DFirst | 1. 逐行扫描表 2. 找到第一条匹配记录立即返回 (不保证顺序) |
DFirst
≈ SELECT TOP 1
SELECT TOP 1
远快于 DFirst
SELECT
直接定位到记录,而 DFirst
仍需逐行检查条件DFirst
可能更慢SELECT
可利用查询缓存场景 | 推荐方法 | 理由 |
---|---|---|
数据量小 | DFirst |
代码简洁,性能损失可忽略 |
数据量大 | SELECT TOP 1 |
利用索引快速定位,性能优势明显 |
需要排序 | SELECT TOP 1 |
DFirst 不保证返回顺序,可能得到非预期记录 |
非技术人员维护 | DFirst |
函数式思维更易理解(如 "查一个值,再加多少") |
复杂条件嵌套 | DFirst |
避免多层子查询,提高可读性 |
查询作为表达式频繁使用 | SELECT TOP 1 |
可通过视图或内联查询优化,减少重复计算 |
无论使用哪种方法,以下操作都能显著提升速度:
添加索引
[002所有交易日].交易日
和 [002所有交易日].ID
上创建索引[001网友投资记录].清仓日
上创建索引预计算常用值
如果 持股天数
是固定规则计算的结果,考虑添加计算字段或使用视图:sql
CREATE VIEW vw_投资记录 WITH SCHEMABINDING AS
SELECT
ID, 清仓日, 股票, 代码,
DATEDIFF('d', 买入日, 清仓日) AS 持股天数, -- 示例计算
收入, 买入时间
FROM
[001网友投资记录];
批量处理替代逐行计算
如果可能,将需要 DFirst
的计算逻辑移到应用层(如 Python 脚本),批量处理数据。
优先使用 JOIN
对于大数据量查询,JOIN
通常是性能最优的选择:sql
SELECT
FORMAT(b.交易日, "YY年mm月dd日") AS 买入日期,
a.*
FROM
[001网友投资记录] a
JOIN [002所有交易日] c ON a.清仓日 = c.交易日
JOIN [002所有交易日] b ON b.ID = c.ID + a.持股天数;
仅在必要时使用 DFirst
始终测试性能
在生产环境前,用真实数据量对比不同方案的执行时间,选择最适合的实现。
你的直觉是对的 —— DFirst
更符合人类思维,但不一定最快。在 Access 这种桌面数据库中,当数据量不大时,牺牲一点性能换取代码可读性是完全值得的。但对于企业级应用或大数据场景,建议优先考虑 JOIN
或优化后的 SELECT
查询。
同花顺APP/招商证券 APP 中的收益分析持仓天数通常是按照交易日来计算的。
具体而言,它会从买入股票的日期开始,到卖出股票的日期为止,只统计证券交易所正常开放交易的日子,周末、法定节假日以及股票停牌日等非交易日都不会计算在内。
例如,投资者在 2024 年 1 月 10 日购买了某股票,2024 年 4 月 15 日卖出,自然日差为 95 天,但期间扣除周末和节假日后的交易日可能为 67 天,那么持仓天数就是 67 天。