SQL Perl DBI 无法打印服务器时间戳字段
SQL Server timestamp fields coming out unprintable with Perl DBI
我正在通过带有 DBD::ODBC 的 Perl DBI 模块连接到 SQL Server 2014 数据库。我正在查看的 table 具有以下结构:
CREATE TABLE [dbo].[JonesFrank$EMSM Printouts](
[timestamp] [timestamp] NOT NULL,
[Document Type] [int] NOT NULL,
[Document No_] [nvarchar](20) NOT NULL,
[Line No_] [int] NOT NULL,
[Entry No_] [int] NOT NULL,
[Printout] [image] NULL,
[Date] [datetime] NOT NULL,
CONSTRAINT [JonesFrank$EMSM Printouts[=11=]] PRIMARY KEY CLUSTERED
(
[Document Type] ASC,
[Document No_] ASC,
[Line No_] ASC,
[Entry No_] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
如您所见,它有一个 timestamp
列,巧妙地命名为 'timestamp'。当我在 SQL 服务器应用程序中从这个 table select 值时,它们以长十六进制数字的形式出现,如下所示:
+====================+
| timestamp |
+====================+
| 0x000000000AA531FB |
| 0x000000000AB0F485 |
| 0x000000000AB0F483 |
| 0x000000000A941C0C |
| 0x000000000AA531F5 |
| 0x000000000AA53448 |
+====================+
然而,当我运行一个Perl程序时,使用DBI
和DBD::ODBC
,结果是包含非printable字符的字符串:
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
use Getopt::Long;
GetOptions (
'dbname=s' => \my $dbname,
'driver=s' => \my $driver,
'server=s' => \my $server,
'user=s' => \my $user,
'pwd=s' => \my $pwd,
) or die "Invalid command line options\n";
die "--dbname <database name> required!\n" unless defined $dbname;
$driver //= 'SQL Server';
$server //= 'SQL';
$user //= 'xxx';
$pwd //= 'xxxxxxxx';
my $table = 'TheTable';
my $dbh = DBI->connect(
"dbi:ODBC:Driver={$driver};Server=$server;UID=$user;PWD=$pwd",
{ RaiseError => 1}
) or die "Cannot connect: $DBI::errstr";
$dbh->do("use $dbname");
my $sql = "SELECT [timestamp] FROM [$table] WHERE Printout IS NOT NULL";
my $sth = $dbh->prepare($sql);
$sth->execute();
while (my $row = $sth->fetch()) {
print $row->[0], "\n";
}
(我必须 post 将输出作为图像,因为我不知道如何通过在此处的键盘上键入来重现输出的字符串...)
我正在为 cygwin 使用 perl 5.22.2、DBI.pm 1.636 和 DBD/ODBC.pm 1.52.
有谁知道为什么时间戳值会这样出现,以及如何获取我在 SQL 服务器应用程序中看到的 'real' 值?
我可以在控制台输出中看到原始数据。 chr(0x0A)
是换行符,chr(0xA5)
是奇怪的字符,chr(0x31)
是数字1
,chr(0xFB)
是另一个奇怪的字符,等等
因此 SQL 服务器应用程序中显示的 64 位整数被编码为字符串。当您听到 "integer"、"string" 和 "encoding" 时,您应该会想到 "pack"
and "unpack"
.
经过反复试验,我发现用于从输出到 Perl 的 SQL 服务器的十六进制字符串重新创建的 Perl 代码如下所示:
sub rawTimestampToHex {
my $i = shift;
sprintf "0x%016X", unpack("Q>",$i);
}
其中 "Q>"
模板意味着将输入的前 8 个字节解释为 big-endian 字节顺序的压缩无符号四元组(64 位整数)值(最高有效位在前)
示例:
print rawTimestampToHex("\x00\x00\x00\x00\x0A\xA5\x31\xFB")
0x000000000AA531FB
我正在通过带有 DBD::ODBC 的 Perl DBI 模块连接到 SQL Server 2014 数据库。我正在查看的 table 具有以下结构:
CREATE TABLE [dbo].[JonesFrank$EMSM Printouts](
[timestamp] [timestamp] NOT NULL,
[Document Type] [int] NOT NULL,
[Document No_] [nvarchar](20) NOT NULL,
[Line No_] [int] NOT NULL,
[Entry No_] [int] NOT NULL,
[Printout] [image] NULL,
[Date] [datetime] NOT NULL,
CONSTRAINT [JonesFrank$EMSM Printouts[=11=]] PRIMARY KEY CLUSTERED
(
[Document Type] ASC,
[Document No_] ASC,
[Line No_] ASC,
[Entry No_] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
如您所见,它有一个 timestamp
列,巧妙地命名为 'timestamp'。当我在 SQL 服务器应用程序中从这个 table select 值时,它们以长十六进制数字的形式出现,如下所示:
+====================+
| timestamp |
+====================+
| 0x000000000AA531FB |
| 0x000000000AB0F485 |
| 0x000000000AB0F483 |
| 0x000000000A941C0C |
| 0x000000000AA531F5 |
| 0x000000000AA53448 |
+====================+
然而,当我运行一个Perl程序时,使用DBI
和DBD::ODBC
,结果是包含非printable字符的字符串:
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
use Getopt::Long;
GetOptions (
'dbname=s' => \my $dbname,
'driver=s' => \my $driver,
'server=s' => \my $server,
'user=s' => \my $user,
'pwd=s' => \my $pwd,
) or die "Invalid command line options\n";
die "--dbname <database name> required!\n" unless defined $dbname;
$driver //= 'SQL Server';
$server //= 'SQL';
$user //= 'xxx';
$pwd //= 'xxxxxxxx';
my $table = 'TheTable';
my $dbh = DBI->connect(
"dbi:ODBC:Driver={$driver};Server=$server;UID=$user;PWD=$pwd",
{ RaiseError => 1}
) or die "Cannot connect: $DBI::errstr";
$dbh->do("use $dbname");
my $sql = "SELECT [timestamp] FROM [$table] WHERE Printout IS NOT NULL";
my $sth = $dbh->prepare($sql);
$sth->execute();
while (my $row = $sth->fetch()) {
print $row->[0], "\n";
}
(我必须 post 将输出作为图像,因为我不知道如何通过在此处的键盘上键入来重现输出的字符串...)
我正在为 cygwin 使用 perl 5.22.2、DBI.pm 1.636 和 DBD/ODBC.pm 1.52.
有谁知道为什么时间戳值会这样出现,以及如何获取我在 SQL 服务器应用程序中看到的 'real' 值?
我可以在控制台输出中看到原始数据。 chr(0x0A)
是换行符,chr(0xA5)
是奇怪的字符,chr(0x31)
是数字1
,chr(0xFB)
是另一个奇怪的字符,等等
因此 SQL 服务器应用程序中显示的 64 位整数被编码为字符串。当您听到 "integer"、"string" 和 "encoding" 时,您应该会想到 "pack"
and "unpack"
.
经过反复试验,我发现用于从输出到 Perl 的 SQL 服务器的十六进制字符串重新创建的 Perl 代码如下所示:
sub rawTimestampToHex {
my $i = shift;
sprintf "0x%016X", unpack("Q>",$i);
}
其中 "Q>"
模板意味着将输入的前 8 个字节解释为 big-endian 字节顺序的压缩无符号四元组(64 位整数)值(最高有效位在前)
示例:
print rawTimestampToHex("\x00\x00\x00\x00\x0A\xA5\x31\xFB")
0x000000000AA531FB