【发布时间】:2023-03-26 07:52:02
【问题描述】:
我的项目中有一个单元用于执行用户之间的消息交换。为此,我使用了两个 MySQL 表:
CREATE TABLE `tmessages` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`header` varchar(100) DEFAULT NULL,
`body` text,
...
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
那是主人。它包含有关发件人、标头、消息正文等的信息和
CREATE TABLE `tmessage_recipients` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`idmessage` int(10) unsigned NOT NULL DEFAULT '0',
`idstuff_recipient` int(10) unsigned NOT NULL DEFAULT '0',
...,
PRIMARY KEY (`id`,`idmessage`,`idstuff_recipient`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
作为详细信息,包括消息发送到的用户、打开消息的日期/时间等(其余信息由触发器设置)以下代码正常工作并发送消息。
var
s: string;
idmessage: longword;
i: Word;
...
s := 'INSERT INTO `tmessages` SET ' + '`header`=' + QuotedStr(Edit12.Text) +
', ' + '`body`=' + QuotedStr(Memo3.Text);
Hdm1.FDConnection1.ExecSQL(s);
idmessage := Hdm1.FDConnection1.ExecSQLScalar('SELECT `id` FROM `tmessages` '
+ 'WHERE `header`=' + QuotedStr(Edit12.Text) + 'AND `body`=' +
QuotedStr(Memo3.Text) + ' ORDER BY `id` DESC LIMIT 1');
for i := 0 to ListBox3.Count - 1 do
begin
s := 'INSERT INTO `tmessage_recipients` SET ' + '`idmessage`=' +
inttostr(idmessage) + ', ' + '`idstuff_recipient`=' +
inttostr(ListBox3.ItemByIndex(i).Tag);
Hdm1.FDConnection1.ExecSQL(s);
end;
但我认为这不是最优的,因为我必须先获取 idmessage。如何优化程序并使其更可靠?可以在Hdm1.FDConnection1.ExecSQL() 的一次调用中完成插入吗?
编辑
使用 @Alex Tartan 和 @whosrdaddy 的 cmets,我得到了这样的结果。这是最佳方式吗?还是我需要改变一些东西?
s:= 'INSERT INTO `tmessages` (`header`,`body`) VALUES(:p1, :p2);'#13#10 +
'SET @last_id := (SELECT LAST_INSERT_ID());';
for i := 0 to ListBox3.Count - 1 do
s := s + #13#10'INSERT INTO `tmessage_recipients` (`idmessage`,`idstuff_recipient`)'
+ ' VALUES(@last_id,' + inttostr(ListBox3.ItemByIndex(i).Tag) + ');';
Hdm1.FDConnection1.ExecSQL(s, [Edit12.Text, Memo3.Text]);
编辑 2
在@kobik 的提议下,我得出以下结论:
s:= 'INSERT INTO `tmessages` (`header`,`body`) VALUES(:p1, :p2);'#13#10 +
'SET @last_id := (SELECT LAST_INSERT_ID());';
for i := 0 to ListBox3.Count - 1 do
s := s + #13#10'INSERT INTO `tmessage_recipients` (`idmessage`,`idstuff_recipient`)'
+ ' VALUES(@last_id,' + inttostr(ListBox3.ItemByIndex(i).Tag) + ');';
HDM1.FDQViaFDTransaction.Transaction.StartTransaction;
try
HDM1.FDQViaFDTransaction.ExecSQL(s, [Edit12.Text, Memo3.Text]);
HDM1.FDQViaFDTransaction.Transaction.Commit;
except
HDM1.FDQViaFDTransaction.Transaction.Rollback;
end;
编辑 3
在@Rick-James 的宝贵建议下,代码被转换为以下...
s := 'INSERT INTO `tmessages` (`header`,`body`) VALUES(:p1, :p2);'#13#10 +
'SET @last_id := (SELECT LAST_INSERT_ID());';
s := s + #13#10'INSERT INTO `tmessage_recipients` ' +
'(`idmessage`,`idstuff_recipient`)'#13#10'VALUES';
for i := 0 to ListBox3.Count - 1 do
s := s + #13#10'(@last_id,' + inttostr(ListBox3.ItemByIndex(i).Tag) + '),';
s[High(s)] := ';';
Hdm1.FDQViaFDTransaction.Transaction.StartTransaction;
try
Hdm1.FDQViaFDTransaction.ExecSQL(s, [Edit12.Text, Memo3.Text]);
Hdm1.FDQViaFDTransaction.Transaction.Commit;
ShowMessage('Message sent.');
Button10Click(self);
except
Hdm1.FDQViaFDTransaction.Transaction.Rollback;
end;
【问题讨论】:
-
请停止使用串联查询,您很容易受到 SQL 注入的攻击。改用参数...
-
更好地使用事务
-
谢谢@whosrdaddy,我进行了编辑。您认为我需要更改其他内容吗?
-
谢谢你,@kobik。您是指一些中间层(如
DataSnap)还是从FDQuery调用查询? -
您需要一个事务来保持数据的完整性。查看
TFDTransaction或使用mysqlSTART TRANSACTION/COMMIT... 另外,如果您有新问题,请发布新问题。
标签: mysql delphi query-optimization master-detail