【问题标题】:Why is MySQL giving me a syntax error?为什么 MySQL 给我一个语法错误?
【发布时间】:2017-01-10 16:07:15
【问题描述】:

所以我在编码方面几乎是一个大新手,我通过反复试验学习,这次我尝试使用 Perl 使用数据库,实际上当我只使用 3 个表头(终端, Zeitstempel,Ergebnisnummer)但是当我尝试添加更多标题(Ergebnistext,Transart,Belegnummer,Trace,Betrag,Kartenart,Kartennummer,Entrymode)时,它总是给我一个语法错误,我检查了半个小时但找不到错误.如果有人知道为什么我会很感激你的帮助!我的这部分代码如下所示:

            foreach $file (@file)
        {
            $currentfile = "$currentdir\\$file";
            open(zloop, "<", $currentfile) or die "Failed to open file: $!\n";
                    while ( <zloop> ) {
                    my %row;
                    chomp;
                    @row{@headers} = split /;/;
                    my $tid = $row{'tid'}; 
                    my $zeit = $row{'zeit'};
                    my $ergebnisnummer = $row{'ergebnisnummer'};
                    my $ergebnistext = $row{'ergebnistext'};
                    my $transart = $row{'transart'};
                    my $belegnummer = $row{'belegnummer'};
                    my $trace = $row{'trace'};
                    my $betrag = $row{'betrag'};
                    my $kartenart = $row{'kartenart'}; 
                    my $kartennummer = $row{'kartennummer'};
                    my $entrymode = $row{'entrymode'}; 

    my $sth = $dbh->prepare("INSERT INTO `teso`( Terminal, Zeitstempel, Ergebnisnummer, Ergebnistext, Transart, Belegnummer, Trace, Betrag, Kartenart, Kartennummer, Entrymode ) 
    values ( $tid, $zeit, $ergebnisnummer, $ergebnistext, $transart, $belegnummer, $trace, $betrag, $kartenart, $kartennummer, $entrymode )");
    $sth->execute() or die $DBI::errstr;
    $sth->finish();

我没有将变量名称更改为英文,因为它们可能会导致语法错误。这是错误信息:

DBD::mysql::st 执行失败:您的 SQL 语法有错误; 检查与您的 MySQL 服务器版本相对应的手册 在第 2 行的 '' )' 附近使用的正确语法 C:\Users\Desktop\findlogstamp\sqlneu.pl lin e 50, line 1. 你 您的 SQL 语法有错误;检查对应的手册 您的 MySQL 服务器版本,以便在 '' )' 附近使用正确的语法 第 2 行位于 C:\User s\Desktop\findlogstamp\sqlneu.pl 第 50 行, 第 1 行。

【问题讨论】:

  • 错误是什么?
  • 通过将变量插入字符串来构建 SQL 语句通常是一个非常糟糕的主意 - 有关详细信息,请参阅 Bobby Tables

标签: mysql perl


【解决方案1】:

由于将变量作为字符串传递,您可能会遇到引用错误。您应该改用占位符。

尝试做这样的事情:

my $sth = $dbh->prepare('INSERT INTO `teso`( Terminal, Zeitstempel, Ergebnisnummer, Ergebnistext, 
   Transart, Belegnummer, `Trace`, Betrag, Kartenart, Kartennummer, Entrymode ) 
   values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )');
$sth->execute($tid, $zeit, $ergebnisnummer, $ergebnistext,
   $transart, $belegnummer, $trace, $betrag, $kartenart,
   $kartennummer, $entrymode) or die $DBI::errstr;

我还加入了mwp's answer too

还值得花时间阅读Borodins Answer,以更彻底地处理 perl 中的占位符和 SQL。

【讨论】:

  • 这行得通,非常感谢,您介意解释一下到底出了什么问题吗?我的变量大多是完整的数字,但 2-3 是字符串或变体类型,我认为用 ' ' 标记它们就足够了
  • 你必须精确地引用一些东西,以便 MySQL 能够愉快地处理它们。使用绑定变量(prepare() 中的 ?),然后将您的值传递给 execute() 将为您在需要的地方进行“繁重的”引用。通常推荐将变量传递给语句的方法。
  • 为了记录,我的意思是占位符,而不是绑定变量,它们是不同的东西!
【解决方案2】:

我相信“trace”是 MySQL 中的保留字。尝试在列名周围添加反引号。

【讨论】:

  • 难道不知道它只能在INSERT INTO foo ( ... ) 构造中包含列名吗?
  • 那很好,但不幸的是,没有。
  • ye,之前我有一个错误是由“Terminal-ID”列引起的,-ID 引起了问题,我花了 1 小时才意识到,所以我已经尝试在 `` 中添加跟踪但可悲的是它没有用,谢谢你的好答案!
【解决方案3】:

必须正确引用 SQL 表达式中的值。简单的数值不需要引号,我想您使用的前三列——终端 ID、时间戳和结果编号——都是数字,这就是它们不带引号的原因

DBI 模块提供函数数据库句柄方法quote,它将为您执行此操作(以及quote_identifier,它对标识符执行相同的操作,例如表和列名称)。然而,prepare 使用 placeholders 的 SQL 语句总是更安全,而不是自己尝试将值插入到字符串中。然后可以在execute 调用中提供实际值,DBI 将在将所有值插入语句之前隐式调用$dbh-&gt;quote

最好将prepare 声明一次,因为句柄可能会被重复使用。并且无需从您的%row 哈希中提取一系列标量变量——哈希切片 可用于将正确的字段直接从哈希中提取到execute 调用中。但是,最好将字段名称列表保留在数组中,就像您对@headers 所做的那样;事实上你的@headers可能和我的@items一样,在这种情况下不需要同时定义两个

我为 SQL 字符串使用了 here 文档。如果您想做同样的事情,请注意在结束标记 END_SQL 之前或之后必须有 没有空格,否则将找不到它

请注意,我已将 join 与列表重复运算符 x 一起使用,以在 QSL 中生成正确数量的问号占位符。这样做更安全,可以避免计算错误,并允许在不更改 SQL 语句的情况下更改字段数。如果你愿意,可以print "$sql\n"查看已经构建的SQL

希望对你有帮助

my @items = qw/
    tid zeit ergebnisnummer ergebnistext transart
    belegnummer trace betrag kartenart kartennummer entrymode
/;

my $sql = sprintf <<END_SQL, join ', ', ('?') x @items;
INSERT INTO teso (
    Terminal, Zeitstempel, Ergebnisnummer, Ergebnistext, Transart,
    Belegnummer, Trace, Betrag, Kartenart, Kartennummer, Entrymode
)
VALUES (%s)
END_SQL

my $insert = $dbh->prepare($sql);

for my $file ( @file ) {

    my $current_file = "$currentdir\\$file";

    open my $fh, '<', $current_file or die qq{Unable to open "$current_file" for input: $!};

    while ( <$fh> ) {

        my %row;
        @row{@headers} = split /;/;

        $insert->execute(@row{@items}) or die $sth->errstr;
    }
}

【讨论】:

    【解决方案4】:

    我认为,错误是因为teso 周围的反引号是由 perl 插值的。 无论如何,将值插入查询字符串是一个非常糟糕的主意。 您可以用单引号替换查询周围的引号(以消除不需要的插值),并用占位符替换要插入的值。然后你应该将实际值传递给 execute() 方法。像这样:

    my $sth = $dbh->prepare('INSERT INTO teso( Terminal, Zeitstempel, Ergebnisnummer, Ergebnistext, Transart, Belegnummer, Trace, Betrag, Kartenart, Kartennummer, Entrymode ) 
    values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )');
    $sth->execute($tid, $zeit, $ergebnisnummer, $ergebnistext, $transart, $belegnummer, $trace, $betrag, $kartenart, $kartennummer, $entrymode) or die $DBI::errstr;
    $sth->finish();
    

    或者,如果你只想插入一行,你可以用一个 do() 替换所有这些:

    $dbh->do('INSERT INTO teso( Terminal, Zeitstempel, Ergebnisnummer, Ergebnistext, Transart, Belegnummer, Trace, Betrag, Kartenart, Kartennummer, Entrymode ) values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )', undef,
      $tid, $zeit, $ergebnisnummer, $ergebnistext, $transart, $belegnummer, $trace, $betrag, $kartenart, $kartennummer, $entrymode ) or die $DBI::errstr;
    

    【讨论】:

    • 你的第一个假设是错误的。 Perl 不会在双引号内的反引号中运行任何内容。插值不能那样工作。
    • @simbabque,你说得对!更新了答案。
    猜你喜欢
    • 2011-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-11
    • 2011-04-16
    • 1970-01-01
    • 2015-12-11
    相关资源
    最近更新 更多