Perl DBI创建表错误

时间:2014-02-05 21:41:57

标签: mysql perl dbi

我正在尝试使用DBI在perl中创建表。这是我的代码:

#! usr/bin/perl
use strict;
use warnings;
use 5.014;
use DBI;

my $user = "user";
my $password = "password";
my $hostname = "localhost";
my $database = 'database';

my $dsn = "DBI:mysql:database=$database;host=$hostname;";
my $dbh = DBI->connect($dsn, $user, $password, { RaiseError => 1});


my $create = "-- drop table if exists DEVICE;
CREATE TABLE DEVICE(
    NAME          CHAR(60),
    ID            CHAR(36)    NOT NULL,
    SHORT_ID      INT UNSIGNED,
    MODEL         CHAR(50),
    SERIAL_NUMBER VARCHAR(50),
    IP_ADDRESS    CHAR(50),
    LOCATION      CHAR(50),
    DV_VERSION    CHAR(20),
    OS_VERSION    CHAR(20),
    DEVICE_GROUP  CHAR(50),
    MANAGED       TINYINT     NOT NULL,
    CONSTRAINT PK_DEVICE PRIMARY KEY (ID)
) Engine = InnoDB;

-- drop table if exists SEGMENT;
CREATE TABLE SEGMENT(
    ID               CHAR(100)        NOT NULL,
    DEVICE_ID        CHAR(36)         NOT NULL,
    NAME             CHAR(60),
    IP_ADDRESS       CHAR(50),
    SLOT_INDEX       INT    NOT NULL,
    SEGMENT_INDEX    INT    NOT NULL,
    CONSTRAINT PK_SEGMENT PRIMARY KEY (ID),
    KEY(DEVICE_ID),
    CONSTRAINT FK_PARENT_DEV FOREIGN KEY (DEVICE_ID) REFERENCES DEVICE(ID)
) Engine = InnoDB;
";
my $sth = $dbh->prepare($create);
$sth->execute;

运行时出现以下错误:

DBD::mysql::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CREATE TABLE SEGMENT(
ID               CHAR(100)        NOT NULL,
DEVICE' at line 18 at file.pl line 47.

当我在mysql中直接复制/粘贴$ create中完全相同的字符串时,它可以很好地工作。

请帮助,谢谢。

编辑:对不起,我应该澄清一下。我正在从另一个来源中提取$ create中的内容。这只是其中的一部分,我无法编辑文本。我想我需要知道如何将它拆分成一个数组并逐个执行它们。

3 个答案:

答案 0 :(得分:2)

DBI连接不像MySQL命令行工具那样工作。您不能在一个SQL语句中执行多个SQL语句,并且不需要--前缀。

(值得注意的是,你已经颠倒了大写SQL语言和小写标识符的惯例。)

这样的代码应该有效

$dbh->do("drop table if exists DEVICE");
$dbh->do(<<__ENDSQL__);
CREATE TABLE DEVICE(
    NAME          CHAR(60),
    ID            CHAR(36)    NOT NULL,
    SHORT_ID      INT UNSIGNED,
    MODEL         CHAR(50),
    SERIAL_NUMBER VARCHAR(50),
    IP_ADDRESS    CHAR(50),
    LOCATION      CHAR(50),
    DV_VERSION    CHAR(20),
    OS_VERSION    CHAR(20),
    DEVICE_GROUP  CHAR(50),
    MANAGED       TINYINT     NOT NULL,
    CONSTRAINT PK_DEVICE PRIMARY KEY (ID)
) Engine = InnoDB
__ENDSQL__

$dbh->do("drop table if exists SEGMENT");
$dbh->do(<<__ENDSQL__);
CREATE TABLE SEGMENT(
    ID               CHAR(100)        NOT NULL,
    DEVICE_ID        CHAR(36)         NOT NULL,
    NAME             CHAR(60),
    IP_ADDRESS       CHAR(50),
    SLOT_INDEX       INT    NOT NULL,
    SEGMENT_INDEX    INT    NOT NULL,
    CONSTRAINT PK_SEGMENT PRIMARY KEY (ID),
    KEY(DEVICE_ID),
    CONSTRAINT FK_PARENT_DEV FOREIGN KEY (DEVICE_ID) REFERENCES DEVICE(ID)
) Engine = InnoDB
__ENDSQL__

答案 1 :(得分:1)

你正在做的一切正确,只是稍作改动,分别执行这两个查询,在$create之后放置此代码:

 my $delim = "-- ";    // delimeter
 my @sqls = split($delim, $create);   //split based on delimiter

 foreach my $sql (@sqls) {   // take each sql string 
      $sql = $delim . $sql;  // append delimiter, as it was removed while splitting the sql string
      my $sth = $dbh->prepare($sql);   // prepare the sql statement
      $sth->execute;   // execute it
 }

 exit 0;

PS:这是经过测试的代码。

答案 2 :(得分:0)

如果从外部源提取原始sql并不是一个糟糕的想法,你可能总是让它变得更糟:

open my $fh, "mysql |";
print $fh "$create\n";
close $fh;

在“mysql”和|管道之间,您需要凭据--user = username等,以及默认数据库的名称。

或者,对于DBI,如果SQL语句正确制作,就像mydqldump生成一样,那么;\n将始终是语句分隔符,所以你应该有合理的运气分裂字符串......事实上,你可以甚至丢掉分号--MySQL实际上并不关心。

如果你有更复杂的东西,比如先前有DELIMITER声明的存储过程,你需要解析这些语句,相应地修改你对分隔符的期望,并丢弃分隔符语句和备用语句分隔符。你不需要,实际上不能使用,分隔符声明和自定义分隔符与DBI - 你只需在一个 - &gt; do()调用中发送过程声明,它就可以工作。

你应该能够放弃--注释行,因为一旦你拆分了分隔符......那些不应该被删除。