我使用的是 Ubuntu 24.04.2 LTS 系统。我正在使用一个 Windows 程序(使用 WINE),它的输出是一个 CSV 文件。我想把这个输出下载到我的电脑上。有时我想把它重新加载到程序中,以便与其他数据进行比较。程序推荐使用 Excel。有什么最优雅的方法来解决这个问题?非常感谢。
主页
自从接受了最近的更新后,LibreOffice 就打不开了。检查后发现它已更新至 LibreOffice 25.2。如果我打开应用中心,它会将 LibreOffice 24.8.6.2 列为最新稳定版本。我已经从应用中心安装了 LibreOffice 24.8.6.2,现在两个版本都列出了,但只有 24.8.6.2 能打开。我尝试使用 Synaptic 卸载 LibreOffice 25.2,但没有成功。当我尝试打开 .odt 文件时,默认版本是 25.2,我必须选择“打开方式”才能使用有效的版本打开。我该如何卸载 LibreOffice 25.2?
我使用的是 Ubuntu 24.04.2 LTS。
好的,解决了——我按照提示运行了“sudo snap remove libreoffice”。它似乎删除了 LibreOffice 24.8.6.2——这个版本之前可以运行,但现在 LibreOffice 25.2 可以运行了?所以问题不知怎么解决了。
一般来说,人们警告不要禁用解锁密钥环的密码(当您登录计算机时,登录密钥环没有解锁,如何才能停止在启动时提示解锁“默认”密钥环?),这避免了在没有密码的情况下自动登录时出现解锁密钥环的提示。
不过,我觉得通过 LUKS 加密驱动器时,安全问题并不重要。我只使用自动登录,因为无论如何我都需要通过 LUKS“登录”,而且我觉得这样足够安全。但考虑到其他帖子里提到的坚定建议,我怀疑自己是否遗漏了什么。
我们在登录后永久解锁密钥环后,是否存在可以再次使用的状态?我想也许当我们退出时,这会成为一个问题,但话说回来,如果你退出,你就无法通过自动登录登录。所以这两种情况(自动登录 + LUKS,常规登录)的行为都是一样的,因为你需要输入密码才能登录,而这无论如何都会导致密钥环被解锁。
也许我无法提出真正的问题,因为使用具有自动登录和解锁密钥环的 LUKS 时没有真正的问题。
我正在尝试使用 Ubuntu 服务器扫描可用的 Wi-Fi 网络。我尝试在不安装任何第三方软件包的情况下完成此操作。
据我所知,在 Ubuntu Server 的安装过程中,向导会建议它找到的一些可用网络,但我不知道它在 ISO 中使用什么程序来执行此操作。
- 不安装任何额外的包可以做到这一点吗?
- 如果没有,我可以安装什么软件包来进行快速扫描?(请注意,我想避免使用 NetworkManager 并继续使用默认的 systemd-networkd)
Ubuntu 22.04
我创建了一个 Debian 包,它必须在用户重新启动机器后执行一些安装后的工作。
我有一个执行重启后工作的脚本,让它在下次重启后运行一次的正确方法是什么?
我目前的想法是将其注册为运行一次的 systemd 服务,并让它在运行后接触一些虚拟文件,然后如果虚拟文件存在(例如在随后的重启后)就不执行任何操作。
但我想知道是否已经有其他内置方法可以在下次重启后仅运行一次程序。
我一直在用一台运行 Windows XP 的旧电脑。这台电脑连接着一台 Biesse 机器,该机器由一个只能在 Windows XP 上运行的软件操作。
在这台电脑的 boot.ini 文件(位于 c: 目录)中,有一个/execute
开关。我认为这个/execute
命令是机器软件需要的,但我想有人能帮我解释一下。
完整的 boot.ini 文件是:
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /execute /fastdetect
这/execute
没有在 Microsoft boot.ini 规范中列出,如链接中所示:https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/switch-options-for-boot-files
那么,/execute
Windows XP 的 boot.ini 文件中可能意味着什么呢?
我正在尝试使用 ssh-agent 来允许使用带有密码的 ED25519 ssh 密钥进行无密码签名 git 提交。
但是,每次我从命令行提交时,系统都会要求我输入密码。我怀疑这是配置问题,因为 Visual Studio 运行正常,只是命令行或 VS Code 不行。
我使用的是标准的 Windows 环境,而不是 Git Bash 或 WSL。
- ssh-agent 设置为在启动时运行,并已验证
ssh-add -l
显示已添加正确的密钥- 我已设置
GIT_SSH
为 C:\Windows\System32\OpenSSH\ssh.exe
Git 配置明智
core.sshCommand = C:/Windows/System32/OpenSSH/ssh.exe
signingkey
设置为添加到的键ssh-agent
commit.gpgsign = true
gpg.format = ssh
操作系统 Windows 10 22H2 git 2.49.0.windows.1 终端 1.22.10731.0 Powershell 7.5.0
我不得不重新镜像我的系统(Ubuntu 22.04 LTS),原因我就不多说了。我之前有个很好的备份。
我想找回我之前的 Firefox 书签,所以我改成了profiles.ini
指向已恢复配置文件目录的副本。这个方法成功了,也恢复了我的旧书签,但我认为配置文件中的其他地方可能损坏了,导致 Firefox 每五分钟就会崩溃一次。这是新出现的问题。之前 Firefox 运行得很好。
我不想忍受这种情况,所以我决定不使用整个恢复的配置文件,而只传输书签。
因此,我的个人资料仍设置为恢复的版本,我将书签导出为 HTML。
然后我将我的配置文件重置为新的并尝试导入该bookmarks.html
文件,但 Firefox 告诉我文件中没有书签。
我在这里遗漏了什么?有没有办法从另一个配置文件导入书签?
我正在使用深色主题。正如您所见,错误(来自 Python 的 ModuleNotFound)几乎无法阅读。我有两个问题。
- 这个颜色是由 python 还是终端控制的?
- 如何更改颜色?是否有任何 LS_COLOR 设置?
我观察到,在 Ubuntu 24.04.2coreutils
版本上9.4-3ubuntu6
运行:
$ tail -c 4097 /dev/zero
$ echo $?
0
立即退出,状态码为 0。我预计该命令将无限期阻塞,因为 /dev/zero 是一个无休止的流。
相反,以下命令的行为符合预期(即,它们会阻塞直到被中断):
$ tail -c 4096 /dev/zero
^C
$ echo $?
130
$ cat /dev/zero | tail -c 4097
^C
$ echo $?
130
调试尝试
strace 输出显示了两次调用之间的差异:
strace tail -c 4096 /dev/zero | strace tail -c 4097 /dev/zero |
---|---|
… | … |
关闭(3)= 0 | 关闭(3)= 0 |
openat(AT_FDCWD, “/dev/zero”, O_RDONLY) = 3 | openat(AT_FDCWD, “/dev/zero”, O_RDONLY) = 3 |
fstat(3,{st_mode=S_IFCHR|0666,st_rdev=makedev(0x1, 0x5),…}) = 0 | fstat(3,{st_mode=S_IFCHR|0666,st_rdev=makedev(0x1, 0x5),…}) = 0 |
lseek(3,-4096,SEEK_END) = 0 | lseek(3,-4097,SEEK_END) = 0 |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | 读取(3,“\0\0\0\0\0\0\0\0\0\0\…,4097)= 4097 |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | fstat(1,{st_mode=S_IFIFO|0600,st_size=0,…}) = 0 |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | 写入(1,“\0\0\0\0\0\0\0\0\0\0\…,4096 |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | 关闭(3)= 0 |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | 写入(1,“\0”,1)= 1 |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | 关闭(1)= 0 |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | 关闭(2)= 0 |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | 退出组(0)=? |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | ~~+~~ 以 0 退出 ~~+~~ |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | |
读取(3,“\0\0\0\0\0\0\0\0\0\00”…,8192)= 8192 | |
… |
为了从 PHP 网站向自己发送 2FA 电子邮件,我设置了 MX 记录覆盖并创建了允许访问目标域的邮件服务器的防火墙规则,但是当我尝试发送时,sendmail
在其日志中写入电子邮件已被接受但从未发送。
为了测试 DNS 名称解析,我运行nslookup
并输入type=MX
命令,但它总是打印:
;; communications error to 10.0.0.1#53: timed out
;; communications error to 10.0.0.1#53: timed out
;; communications error to 10.0.0.1#53: timed out
;; no servers could be reached
当我ping
、telnet
或curl
等操作时,该 DNS 服务器的名称解析工作正常。例如,它可以从 Debian 仓库安装软件包。
sendmail
从我所能吸收的关于 的少量信息来看,听起来它需要传递到 的只是一个指向 的[email protected]
MX 记录。如果我错了,请纠正我。所有这些项目似乎都已到位:在我的 DNS 服务器上有一个 MX 记录;它指向也有一个 A 记录;我正在发送到;有一条防火墙规则允许带有的 Linux 计算机使用 DNS 服务器解析名称;有一条防火墙规则允许带有的 Linux 计算机向 发送电子邮件。所有这些都存在于我的 LAN 中,不需要访问外部 Internet。example.com
smtp.example.com
example.com
smtp.example.com
[email protected]
sendmail
sendmail
smtp.example.com
在这种情况下我应该如何排除邮件发送故障?
编辑:
同时,我发现了失败的根本原因sendmail
:它会在命令行上使用的电子邮件地址前面加上当前登录的用户名(不考虑su
),即如果我的用户名是myusername
,那么它会在第一次尝试和第二次尝试时更改me@mydomain
为。myusername@me@mydomain
myusername@mydomain
缺少了一些东西sendmail.mc
,但是它是什么呢?
这是一个脚本,用于修复文件从 Windows 移动到 Mac 时损坏的西里尔文文件名(基于对使用不同编码后文件名被乱码后的恢复文件名的回答)
#!/bin/zsh
# Usage: <script> <target directory>
# Requires Perl::Rename
find "$1" -mindepth 1 -print0 |
rename -0 -d -e '
use Unicode::Normalize qw(NFC);
use Encode qw(:all);
if ($_ =~ /[†°Ґ£§•¶І®©™Ђђ≠]/) {
my $check = DIE_ON_ERR | LEAVE_SRC;
my $new = eval {encode("UTF-8",
decode("cp866",
encode("mac-cyrillic",
NFC(decode("UTF-8", $_, $check)), $check), $check))
};
if ($new) {$_ = $new;} else {warn $@;}
}'
我希望它仅重命名目标目录中文件名中至少包含以下字符之一的文件:†°Ґ£§•¶І®©™Ђђ≠
。但由于某种原因,脚本会重命名那里的所有文件:例如,正确的文件名срочно.txt
更改为无意义的ёЁюўэю.txt
。我做错了什么?
我的测试文件夹的路径很简单/Users/john/scripts/test
:没有空格,也没有西里尔字母或特殊字符。
该脚本在 macOS 和 BSD 版本上使用find
。
问题得到解答两天后的更新:Stéphane 的 Chazelas 和 Choroba 的版本对我来说很好用。Terdon 的版本对我来说还不行。
我感觉这是一个相对简单的问题,但半个小时后,我仍然不知道如何解决它。
在 PostgreSQL 查询WHERE
子句中,我需要引用一个值而不是直接使用它。该查询将在 Ruby 脚本中执行,我无法直接从我的环境中插入需要获取的值。
我的查询是
CREATE MATERIALIZED VIEW my_mat_view AS
SELECT ...
...
WHERE occurred_at BETWEEN NOW() - INTERVAL '?? HOURS' AND NOW()
用于计算间隔的小时数在INTERVAL_HOURS
环境变量中定义。如上所述,我可以??
用插值替换,因为它在单引号内。
我的想法是使用变量(或产生相同结果的东西)将插值移到单引号之外。
我发现最接近解决方案的选项是
DO $$
DECLARE myVar INT;
BEGIN
myVar := #{ENV['INTERVAL_HOURS'};
CREATE MATERIALIZED VIEW my_mat_view AS
SELECT ...
...
WHERE occurred_at BETWEEN NOW() - INTERVAL 'myVar HOURS' AND NOW()
END $$;
应该可以正确插值,但会产生
Query 1 ERROR at Line 1: : ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function inline_code_block line 5 at SQL statement
我尝试SELECT
用替换PERFORM
,但视图未创建(并且未返回任何错误)。
我这里漏掉了什么?我可以采取其他方法吗(例如,WITH
子句)?
我正在阅读这篇文章 并做了以下事情:
go
create table fiirst (
col1 int,
col2 int
);
create table seecond(
col1 int,
col2 int
);
with
n1(c) as (select 0 union all select 0 ),
n2(c) as ( select 0 from n1 as t1 cross join n1 as t2),
n3(c) as ( select 0 from n2 as t1 cross join n2 as t2),
n4(c) as (select 0 from n3 as t1 cross join n3 as t2),
ids(id) as (select ROW_NUMBER() over (order by (select null)) from n4)
insert into fiirst(col1,col2)
select id,id
from ids;
with
n1(c) as (select 0 union all select 0 ),
n2(c) as ( select 0 from n1 as t1 cross join n1 as t2),
n3(c) as ( select 0 from n2 as t1 cross join n2 as t2),
n4(c) as (select 0 from n3 as t1 cross join n3 as t2),
ids(id) as (select ROW_NUMBER() over (order by (select null)) from n4)
insert into seecond(col1,col2)
select id,id
from ids;
----Craig Freedman's query
select *
from fiirst
where fiirst.col1 > (
select min(seecond.col1)
from seecond
where seecond.col2 < fiirst.col2
);
尽管表本身是堆,但我还是得到了一个索引假脱机。问题是,这是怎么发生的?为什么我会在堆上得到一个索引假脱机?在上面链接中提到的例子中,没有行,所以没有假脱机,但在这里我却看到了?
在刷新 Postgres 中的物化视图后,我想立即知道其中有多少行。
目前我通过运行第二个 SQL 命令来执行此操作(SELECT count(*) FROM
...)
有没有更有效的方法? REFRESH 命令可以返回行数吗?
我的 T-SQL 脚本出了点问题。我正在思考如何让它输出预期的输出。
这是我的 T-SQL 脚本:
DECLARE @user_id VARCHAR(50) = 'UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL'; -- FOR TESTING PURPOSES ONLY
DECLARE @status_t TABLE (status VARCHAR(50));
INSERT INTO @status_t VALUES ('PENDING'),('APPROVED'),('PROCESSING')
SELECT
orders.batch_code, -- FOR DEMONSTRATION PURPOSES ONLY
oa_prod.nested_data AS nested_prod,
cnb.amount_to_pay
FROM
[testDB].[dbo].[Orders] AS orders
RIGHT OUTER JOIN
[testDB].[dbo].[Payment] AS cnb
ON
orders.order_id = cnb.order_id
LEFT OUTER JOIN
[testDB].[dbo].[StatusRef] AS stat_ref
ON
orders.stat_ref_id = stat_ref.stat_ref_id
CROSS APPLY( -- OR OUTER APPLY
SELECT
orders.batch_code,
orders.order_id,
prod.prod_name,
pv_kn.key_name,
pv_kv.value,
CASE WHEN
orders.prod_id IS NOT NULL
THEN
prod.disc_price
WHEN
orders.prod_var_id IS NOT NULL
THEN
prod_var.disc_price
END AS disc_price,
orders.quantity
FROM
[testDB].[dbo].[Product] AS prod
RIGHT OUTER JOIN
[testDB].[dbo].[SubProduct] AS prod_var
ON
prod.prod_id = prod_var.prod_id
LEFT OUTER JOIN
[testDB].[dbo].[SubProductVarKeyValue] AS pv_kv
ON
prod_var.prod_var_id = pv_kv.prod_var_id
LEFT OUTER JOIN
[testDB].[dbo].[SubProductVarKeyNames] AS pv_kn
ON
pv_kv.pv_key_name_id = pv_kn.pv_key_name_id
WHERE
prod.prod_id = orders.prod_id
OR prod_var.prod_var_id = orders.prod_var_id
FOR JSON PATH,
INCLUDE_NULL_VALUES
) AS oa_prod(nested_data) -- it's a syntax analyzer lint conflict ONLY
WHERE
orders.disable = 0
AND cnb.disable = 0
AND orders.user_id = @user_id
AND stat_ref.com_usd_wrds IN (SELECT status FROM @status_t)
ORDER BY
orders.dt_stamp DESC,
orders.batch_code ASC -- To prevent batch_code from being separated
FOR JSON PATH,
INCLUDE_NULL_VALUES
这是我使用表变量的最小、可重复的示例:
DECLARE @StatusRef TABLE(
stat_ref_id VARCHAR(50) PRIMARY KEY NOT NULL,
com_usd_wrds NVARCHAR(100) NOT NULL
);
DECLARE @Product TABLE(
prod_id VARCHAR(50) PRIMARY KEY NOT NULL,
prod_name VARCHAR(200) NOT NULL,
stock INT NOT NULL,
disc_price DECIMAL(12, 2) NOT NULL
);
DECLARE @SubProduct TABLE(
prod_var_id VARCHAR(50) PRIMARY KEY NOT NULL,
stock INT NOT NULL,
disc_price DECIMAL(12, 2) NOT NULL,
prod_id VARCHAR(50) NOT NULL
);
DECLARE @Orders TABLE(
order_id VARCHAR(50) PRIMARY KEY NOT NULL,
batch_code VARCHAR(50) NULL,
quantity INT NOT NULL,
stat_ref_id VARCHAR(50) NOT NULL,
disable BIT DEFAULT (0) NOT NULL,
dt_stamp DATETIME NOT NULL,
prod_id VARCHAR(50) NULL,
prod_var_id VARCHAR(50) NULL,
user_id VARCHAR(50) NOT NULL
);
DECLARE @Payment TABLE(
amount_to_pay DECIMAL(14, 2) NOT NULL,
order_id VARCHAR(50) NOT NULL,
disable BIT DEFAULT (0) NOT NULL
);
DECLARE @SubProductVarKeyValue TABLE(
value VARCHAR(100) NOT NULL,
prod_var_id VARCHAR(50) NOT NULL,
pv_key_name_id VARCHAR(50) NOT NULL
);
DECLARE @SubProductVarKeyNames TABLE(
pv_key_name_id VARCHAR(50) PRIMARY KEY NOT NULL,
key_name VARCHAR(100) NOT NULL
);
INSERT INTO @StatusRef
VALUES
(
'STAT-REF-1001', -- stat_ref_id
'PENDING' -- com_usd_wrds
),
(
'STAT-REF-1002', -- stat_ref_id
'APPROVED' -- com_usd_wrds
),
(
'STAT-REF-1003', -- stat_ref_id
'PROCESSING' -- com_usd_wrds
);
INSERT INTO @Product
VALUES
(
'PROD-ID-1001', -- prod_id
'iPhone', -- prod_name
0, -- stock | dependent to @SubProduct
0.00 -- disc_price | dependent to @SubProduct
),
(
'PROD-ID-1002', -- prod_id
'Samsung', -- prod_name
0, -- stock | dependent to @SubProduct
0.00 -- disc_price | dependent to @SubProduct
),
(
'PROD-ID-1003', -- prod_id
'Nokia', -- prod_name
75, -- stock
33150.00 -- disc_price
),
(
'PROD-ID-1004', -- prod_id
'Google', -- prod_name
100, -- stock
53509.00 -- disc_price
),
(
'PROD-ID-1005', -- prod_id
'Sony', -- prod_name
0, -- stock | dependent to @SubProduct
0.00 -- disc_price | dependent to @SubProduct
),
(
'PROD-ID-1006', -- prod_id
'Lenovo', -- prod_name
0, -- stock | dependent to @SubProduct
0.00 -- disc_price | dependent to @SubProduct
);
INSERT INTO @SubProduct
VALUES
(
'PROD-VAR-ID-1', -- prod_var_id
25, -- stock
45809.00, -- disc_price
'PROD-ID-1001' -- prod_id
),
(
'PROD-VAR-ID-2', -- prod_var_id
50, -- stock
40209.00, -- disc_price
'PROD-ID-1002' -- prod_id
),
(
'PROD-VAR-ID-3', -- prod_var_id
0, -- stock | dependent to @Product
0.00, -- disc_price | dependent to @Product
'PROD-ID-1003' -- prod_id
),
(
'PROD-VAR-ID-4', -- prod_var_id
0, -- stock | dependent to @Product
0.00, -- disc_price | dependent to @Product
'PROD-ID-1004' -- prod_id
),
(
'PROD-VAR-ID-5', -- prod_var_id
125, -- stock
25809.00, -- disc_price
'PROD-ID-1005' -- prod_id
),
(
'PROD-VAR-ID-6', -- prod_var_id
150, -- stock
49100.00, -- disc_price
'PROD-ID-1006' -- prod_id
);
INSERT INTO @SubProductVarKeyValue
VALUES
(
'new', -- value
'PROD-VAR-ID-1', -- prod_var_id
'PVKN-ID-1' -- pv_key_name_id
),
(
'new', -- value
'PROD-VAR-ID-2', -- prod_var_id
'PVKN-ID-1' -- pv_key_name_id
),
(
'new', -- value
'PROD-VAR-ID-5', -- prod_var_id
'PVKN-ID-1' -- pv_key_name_id
),
(
'new', -- value
'PROD-VAR-ID-6', -- prod_var_id
'PVKN-ID-1' -- pv_key_name_id
)
INSERT INTO @SubProductVarKeyNames
VALUES
(
'PVKN-ID-1', -- pv_key_name_id
'Condition' -- key_name
)
INSERT INTO @Orders
(
order_id,
batch_code,
quantity,
stat_ref_id,
dt_stamp,
prod_id,
prod_var_id,
user_id
)
VALUES
(
'ORDER-2025-04-11-B71D0E2F5D8C', -- order_id
NULL, -- batch_code
1, -- quantity
'STAT-REF-1003', -- stat_ref_id
'2025-04-14 10:17:20.963', -- dt_stamp
NULL, -- prod_id
'PROD-VAR-ID-1', -- prod_var_id
'UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL' -- user_id
),
(
'ORDER-2025-04-11-D95EB033CA40', -- order_id
'BGUID-2025-04-11-6D81B58FAE94', -- batch_code
2, -- quantity
'STAT-REF-1001', -- stat_ref_id
'2025-04-13 09:17:20.963', -- dt_stamp
NULL, -- prod_id
'PROD-VAR-ID-2', -- prod_var_id
'UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL' -- user_id
),
(
'ORDER-2025-04-11-7F04EFA2BB60', -- order_id
'BGUID-2025-04-11-6D81B58FAE94', -- batch_code
2, -- quantity
'STAT-REF-1001', -- stat_ref_id
'2025-04-13 09:17:20.963', -- dt_stamp
'PROD-ID-1003', -- prod_id
NULL, -- prod_var_id
'UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL' -- user_id
),
(
'ORDER-2025-04-10-3F03EAA47686', -- order_id
'BGUID-2025-04-10-20239FD2059F', -- batch_code
1, -- quantity
'STAT-REF-1002', -- stat_ref_id
'2025-04-12 08:17:20.963', -- dt_stamp
'PROD-ID-1004', -- prod_id
NULL, -- prod_var_id
'UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL' -- user_id
),
(
'ORDER-2025-04-10-F4A89E2C4A30', -- order_id
'BGUID-2025-04-10-20239FD2059F', -- batch_code
1, -- quantity
'STAT-REF-1002', -- stat_ref_id
'2025-04-12 08:17:20.963', -- dt_stamp
NULL, -- prod_id
'PROD-VAR-ID-5', -- prod_var_id
'UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL' -- user_id
),
(
'ORDER-2025-04-08-31BD887341FA', -- order_id
NULL, -- batch_code
1, -- quantity
'STAT-REF-1001', -- stat_ref_id
'2025-04-11 07:17:20.963', -- dt_stamp
NULL, -- prod_id
'PROD-VAR-ID-6', -- prod_var_id
'UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL' -- user_id
);
INSERT INTO @Payment
(
amount_to_pay,
order_id
)
VALUES
(
45809.00, -- amount_to_pay
'ORDER-2025-04-11-B71D0E2F5D8C' -- order_id
),
(
146718.00, -- amount_to_pay
'ORDER-2025-04-11-D95EB033CA40' -- order_id
),
(
146718.00, -- amount_to_pay
'ORDER-2025-04-11-7F04EFA2BB60' -- order_id
),
(
79318.00, -- amount_to_pay
'ORDER-2025-04-10-3F03EAA47686' -- order_id
),
(
79318.00, -- amount_to_pay
'ORDER-2025-04-10-F4A89E2C4A30' -- order_id
),
(
49100.00, -- amount_to_pay
'ORDER-2025-04-08-31BD887341FA' -- order_id
);
SELECT * FROM @StatusRef
输出:
stat_ref_id | com_usd_wrds |
---|---|
STAT-REF-1001 | 待办的 |
STAT-REF-1002 | 得到正式认可的 |
STAT-REF-1003 | 加工 |
SELECT * FROM @Product
输出:
产品编号 | 产品名称 | 库存 | disc_price |
---|---|---|---|
产品编号-1001 | iPhone | 0 | 0.00 |
产品编号-1002 | 三星 | 0 | 0.00 |
产品编号-1003 | 诺基亚 | 75 | 33150.00 |
产品编号-1004 | 谷歌 | 100 | 53509.00 |
产品编号-1005 | 索尼 | 0 | 0.00 |
产品编号-1006 | 联想 | 0 | 0.00 |
SELECT * FROM @SubProduct
输出:
产品变量 ID | 库存 | disc_price | 产品编号 |
---|---|---|---|
产品变量ID-1 | 二十五 | 45809.00 | 产品编号-1001 |
产品变量ID-2 | 50 | 40209.00 | 产品编号-1002 |
产品变量ID-3 | 0 | 0.00 | 产品编号-1003 |
产品变量ID-4 | 0 | 0.00 | 产品编号-1004 |
产品变量ID-5 | 125 | 25809.00 | 产品编号-1005 |
产品变量ID-6 | 150 | 49100.00 | 产品编号-1006 |
SELECT * FROM @Orders ORDER BY dt_stamp
输出:
订单编号 | 批次代码 | 数量 | stat_ref_id | 禁用 | 日期戳 | 产品编号 | 产品变量 ID | 用户身份 |
---|---|---|---|---|---|---|---|---|
订单号-2025-04-08-31BD887341FA | 无效的 | 1 | STAT-REF-1001 | 0 | 2025年4月11日 07:17:20.963 | 无效的 | 产品变量ID-6 | UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL |
订单号-2025-04-10-3F03EAA47686 | BGUID-2025-04-10-20239FD2059F | 1 | STAT-REF-1002 | 0 | 2025年4月12日 08:17:20.963 | 产品编号-1004 | 无效的 | UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL |
订单-2025-04-10-F4A89E2C4A30 | BGUID-2025-04-10-20239FD2059F | 1 | STAT-REF-1002 | 0 | 2025年4月12日 08:17:20.963 | 无效的 | 产品变量ID-5 | UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL |
订单号-2025-04-11-7F04EFA2BB60 | BGUID-2025-04-11-6D81B58FAE94 | 2 | STAT-REF-1001 | 0 | 2025-04-13 09:17:20.963 | 产品编号-1003 | 无效的 | UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL |
订单号-2025-04-11-D95EB033CA40 | BGUID-2025-04-11-6D81B58FAE94 | 2 | STAT-REF-1001 | 0 | 2025-04-13 09:17:20.963 | 无效的 | 产品变量ID-2 | UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL |
订单号-2025-04-11-B71D0E2F5D8C | 无效的 | 1 | STAT-REF-1003 | 0 | 2025-04-14 10:17:20.963 | 无效的 | 产品变量ID-1 | UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL |
SELECT * FROM @Payment
输出:
应付金额 | 订单编号 | 禁用 |
---|---|---|
45809.00 | 订单号-2025-04-11-B71D0E2F5D8C | 0 |
146718.00 | 订单号-2025-04-11-D95EB033CA40 | 0 |
146718.00 | 订单号-2025-04-11-7F04EFA2BB60 | 0 |
79318.00 | 订单号-2025-04-10-3F03EAA47686 | 0 |
79318.00 | 订单-2025-04-10-F4A89E2C4A30 | 0 |
45809.00 | 订单号-2025-04-08-31BD887341FA | 0 |
SELECT * FROM @SubProductVarKeyValue
输出:
价值 | 产品变量 ID | pv_key_name_id |
---|---|---|
新的 | 产品变量ID-1 | PVKN-ID-1 |
新的 | 产品变量ID-2 | PVKN-ID-1 |
新的 | 产品变量ID-5 | PVKN-ID-1 |
新的 | 产品变量ID-6 | PVKN-ID-1 |
SELECT * FROM @SubProductVarKeyNames
输出:
pv_key_name_id | 键名 |
---|---|
PVKN-ID-1 | 健康)状况 |
以下是修改后的脚本示例,与提供的第一个脚本类似:
DECLARE @user_id VARCHAR(50) = 'UGUID-2025-01-27-14-09-22-1967-ABCDEFGHIJKL'; -- FOR TESTING PURPOSES ONLY
DECLARE @status_t TABLE (status VARCHAR(50));
INSERT INTO @status_t VALUES ('PENDING'),('APPROVED'),('PROCESSING')
SELECT
orders.batch_code, -- FOR DEMONSTRATION PURPOSES ONLY
oa_prod.nested_data AS nested_prod,
cnb.amount_to_pay
FROM
@Orders AS orders
RIGHT OUTER JOIN
@Payment AS cnb
ON
orders.order_id = cnb.order_id
LEFT OUTER JOIN
@StatusRef AS stat_ref
ON
orders.stat_ref_id = stat_ref.stat_ref_id
CROSS APPLY( -- OR OUTER APPLY
SELECT
orders.batch_code,
orders.order_id,
prod.prod_name,
pv_kn.key_name,
pv_kv.value,
CASE WHEN
orders.prod_id IS NOT NULL
THEN
prod.disc_price
WHEN
orders.prod_var_id IS NOT NULL
THEN
prod_var.disc_price
END AS disc_price,
orders.quantity
FROM
@Product AS prod
RIGHT OUTER JOIN
@SubProduct AS prod_var
ON
prod.prod_id = prod_var.prod_id
LEFT OUTER JOIN
@SubProductVarKeyValue AS pv_kv
ON
prod_var.prod_var_id = pv_kv.prod_var_id
LEFT OUTER JOIN
@SubProductVarKeyNames AS pv_kn
ON
pv_kv.pv_key_name_id = pv_kn.pv_key_name_id
WHERE
prod.prod_id = orders.prod_id
OR prod_var.prod_var_id = orders.prod_var_id
FOR JSON PATH,
INCLUDE_NULL_VALUES
) AS oa_prod(nested_data) -- it's a syntax analyzer lint conflict ONLY
WHERE
orders.disable = 0
AND cnb.disable = 0
AND orders.user_id = @user_id
AND stat_ref.com_usd_wrds IN (SELECT status FROM @status_t)
ORDER BY
orders.dt_stamp DESC
--orders.batch_code ASC -- To prevent batch_code from being separated
FOR JSON PATH,
INCLUDE_NULL_VALUES
不幸的是,当我美化 JSON 输出时,它生成的内容如下:
[
{
"batch_code": null,
"nested_prod": [
{
"batch_code": null,
"order_id": "ORDER-2025-04-11-B71D0E2F5D8C",
"prod_name": "iPhone",
"key_name": "Condition",
"value": "new",
"disc_price": 45809,
"quantity": 1
}
],
"amount_to_pay": 45809
},
{
"batch_code": "BGUID-2025-04-11-6D81B58FAE94",
"nested_prod": [
{
"batch_code": "BGUID-2025-04-11-6D81B58FAE94",
"order_id": "ORDER-2025-04-11-D95EB033CA40",
"prod_name": "Samsung",
"key_name": "Condition",
"value": "new",
"disc_price": 40209,
"quantity": 2
}
],
"amount_to_pay": 146718
},
{
"batch_code": "BGUID-2025-04-11-6D81B58FAE94",
"nested_prod": [
{
"batch_code": "BGUID-2025-04-11-6D81B58FAE94",
"order_id": "ORDER-2025-04-11-7F04EFA2BB60",
"prod_name": "Nokia",
"key_name": null,
"value": null,
"disc_price": 33150,
"quantity": 2
}
],
"amount_to_pay": 146718
},
{
"batch_code": "BGUID-2025-04-10-20239FD2059F",
"nested_prod": [
{
"batch_code": "BGUID-2025-04-10-20239FD2059F",
"order_id": "ORDER-2025-04-10-3F03EAA47686",
"prod_name": "Google",
"key_name": null,
"value": null,
"disc_price": 53509,
"quantity": 1
}
],
"amount_to_pay": 79318
},
{
"batch_code": "BGUID-2025-04-10-20239FD2059F",
"nested_prod": [
{
"batch_code": "BGUID-2025-04-10-20239FD2059F",
"order_id": "ORDER-2025-04-10-F4A89E2C4A30",
"prod_name": "Sony",
"key_name": "Condition",
"value": "new",
"disc_price": 25809,
"quantity": 1
}
],
"amount_to_pay": 79318
},
{
"batch_code": null,
"nested_prod": [
{
"batch_code": null,
"order_id": "ORDER-2025-04-08-31BD887341FA",
"prod_name": "Lenovo",
"key_name": "Condition",
"value": "new",
"disc_price": 49100,
"quantity": 1
}
],
"amount_to_pay": 49100
}
]
但是,我需要的输出看起来像这样:
[
{
"batch_code": null,
"nested_prod": [
{
"batch_code": null,
"order_id": "ORDER-2025-04-11-B71D0E2F5D8C",
"prod_name": "iPhone",
"key_name": "Condition",
"value": "new",
"disc_price": 45809,
"quantity": 1
}
],
"amount_to_pay": 45809
},
{
"batch_code": "BGUID-2025-04-11-6D81B58FAE94",
"nested_prod": [
{
"batch_code": "BGUID-2025-04-11-6D81B58FAE94",
"order_id": "ORDER-2025-04-11-D95EB033CA40",
"prod_name": "Samsung",
"key_name": "Condition",
"value": "new",
"disc_price": 40209,
"quantity": 2
},
{
"batch_code": "BGUID-2025-04-11-6D81B58FAE94",
"order_id": "ORDER-2025-04-11-7F04EFA2BB60",
"prod_name": "Nokia",
"key_name": null,
"value": null,
"disc_price": 33150,
"quantity": 2
}
],
"amount_to_pay": 146718
},
{
"batch_code": "BGUID-2025-04-10-20239FD2059F",
"nested_prod": [
{
"batch_code": "BGUID-2025-04-10-20239FD2059F",
"order_id": "ORDER-2025-04-10-3F03EAA47686",
"prod_name": "Google",
"key_name": null,
"value": null,
"disc_price": 53509,
"quantity": 1
},
{
"batch_code": "BGUID-2025-04-10-20239FD2059F",
"order_id": "ORDER-2025-04-10-F4A89E2C4A30",
"prod_name": "Sony",
"key_name": "Condition",
"value": "new",
"disc_price": 25809,
"quantity": 1
}
],
"amount_to_pay": 79318
},
{
"batch_code": null,
"nested_prod": [
{
"batch_code": null,
"order_id": "ORDER-2025-04-08-31BD887341FA",
"prod_name": "Lenovo",
"key_name": "Condition",
"value": "new",
"disc_price": 49100,
"quantity": 1
}
],
"amount_to_pay": 49100
}
]
注意:是在客户端应用中预先计算的。因此,当重复的被分组amount_to_pay
时,它应该成为一个实例。batch_code
用户订单可能会导致上面显示的理想 JSON 输出发生变化。
有人熟悉我正在处理的问题吗?
我尽可能地不愿意实现通用表表达式 (CTE) 方法。
这篇经典的并发安全文章显然是为一次只更新一行而设计的。在我的例子中,我有一个表值输入,并且希望以并发安全的方式更新每一行。我知道这并不总是可行的,但我希望尽可能地接近这一点。MERGE
这似乎是一个自然的解决方案,但我并不信任它,而且它确实很容易出现 bug。Michael J. Swart 文章中剩下的两种方法是:
- 事务内部的锁提示(更新更常见)
CREATE PROCEDURE s_AccountDetails_Upsert ( @Email nvarchar(4000), @Etc nvarchar(max) )
AS
SET XACT_ABORT ON;
BEGIN TRAN
UPDATE TOP (1) dbo.AccountDetails WITH (UPDLOCK, SERIALIZABLE)
SET Etc = @Etc
WHERE Email = @Email;
IF (@@ROWCOUNT = 0)
BEGIN
INSERT dbo.AccountDetails ( Email, Etc )
VALUES ( @Email, @Etc );
END
COMMIT
- 事务内部的锁提示(插入更常见的)
CREATE PROCEDURE s_AccountDetails_Upsert ( @Email nvarchar(4000), @Etc nvarchar(max) )
AS
SET XACT_ABORT ON;
BEGIN TRAN
INSERT dbo.AccountDetails ( Email, Etc )
SELECT @Email, @Etc
WHERE NOT EXISTS (
SELECT *
FROM dbo.AccountDetails WITH (UPDLOCK, SERIALIZABLE)
WHERE Email = @Email
)
IF (@@ROWCOUNT = 0)
BEGIN
UPDATE TOP (1) dbo.AccountDetails
SET Etc = @Etc
WHERE Email = @Email;
END
COMMIT
我可以调整其中任何一个以使用表变量(例如,我怀疑IF (@@ROWCOUNT = 0)
需要完全删除表变量),但是使用表值输入是否明显表明我们应该选择第一个或第二个解决方案?如果不是,那么应该基于什么做出决定?