这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

连接到 Vertica

本手册介绍了几种用于连接到 Vertica 的方法,包括:

  • 使用 vsql 客户端应用程序直接连接到 Vertica。

  • 安装并配置 Vertica 客户端库,以允许客户端应用程序访问 Vertica。

  • 使用 Vertica 客户端库开发自己的客户端应用程序。

1 - 使用 vsql

vsql 是一种基于字符的交互式前端实用程序,使用它可以键入 SQL 语句并查看结果。它还提供了许多元命令及各种类似于 shell 的功能,有助于编写脚本和自动执行各种任务。

如果使用的是已安装在服务器上的 vsql 客户端,则您可以从以下位置进行连接:

您也可以为其他受支持的平台安装 vsql 客户端

一般注释

  • 为清晰起见,SQL 语句可以分布到多个行。

  • vsql 可以处理采用 UTF-8 编码的输入和输出。必须对运行 vsql 的终端模拟器进行设置,使其正确显示 UTF-8 字符。以下示例显示了 PuTTy 中的设置:

    PuTTY 终端 UTF-8 转换

    另请参阅使用区域设置的最佳实践

  • 通过按 Ctrl+C 取消 SQL 语句。

  • 通过按 Ctrl+R 遍历命令历史记录。

  • 断开用户会话的连接时,任何正在进行的事务会自动回退。

  • 若要查看广泛的结果集,请使用 Linux less 实用程序截断较长的行。

    1. 连接到数据库之前,指定您要将 less 用于查询输出:

      $ export PAGER=less
      
    2. 连接到数据库。

    3. 查询宽表:

      => select * from wide_table;
      
    4. less 提示符中,键入:

      -S
      

    如果运行 vsql 的 shell 失败(崩溃或冻结),则 vsql 进程会继续运行,即使您停止数据库也是如此。在这种情况下,可使用 root 身份登录到正在运行 shell 的计算机,手动停止 vsql 进程。例如: # ps -ef | grep vertica

    fred 2401 1 0 06:02 pts/1 00:00:00 /opt/vertica/bin/vsql -p 5433 -h test01_site01 quick_start_single

    ill -9 2401

1.1 - 安装 vsql 客户端

本页介绍非 FIPS 安装。要在符合 FIPS 的系统上安装,请参阅为 ODBC 和 vsql 安装 FIPS 客户端驱动程序

Linux 和 macOS

要在另一个系统上手动安装 vsql:

  1. 下载 vsql。

  2. 解压缩或安装 vsql:

    • 如果您下载了 .tar,则创建 /opt/vertica/ 目录(如果该目录不存在),将 .tar 复制到其中,导航到它,然后解压缩 .tar

      
      $ mkdir -p /opt/vertica/
      $ cp driver_name.tar.gz /opt/vertica/
      $ tar vzxf driver_name.tar.gz
      
    • 如果您下载了 .rpm,请使用以下命令安装它:

      $ rpm -Uvh driver_name.rpm
      
  3. 可以选择将 vsql 目录添加到您的 PATH 中。例如:

    $ export PATH=$PATH:\opt\vertica\bin
    
  4. 使 vsql 客户端可执行。例如,要允许所有用户运行 vsql:

    $ chmod ugo+x /path/to/vsql
    
  5. 将 shell 区域设置设置为受 vsql 支持的区域设置(哪些?)。例如,在 .profile 中添加:

    export LANG=end_US.UTF-8
    

Windows

要安装 vsql 客户端:

  1. 下载 Windows 客户端驱动安装程序。有关此安装程序中包含的驱动程序的详细信息,请参阅 Windows 客户端驱动程序安装程序

  2. 运行安装程序并按照提示安装驱动程序。

  3. 重新启动系统。

安装驱动程序后,您可以选择将 vsql 目录添加到 PATH。例如,要针对当前会话使用 Windows PowerShell 将 vsql 目录附加到 PATH:

PS C:\> $Env:PATH += ";C:\Program Files\Vertica Systems\VSQL64\"

您可以通过运行 vsql -? 来验证 vsql 目录是否在您的 PATH 中:

PS C:\> vsql -?
This is vsql, the Vertica Analytic Database interactive terminal.

Usage:
  vsql [OPTIONS]... [DBNAME [USERNAME]]

有关用法的详细信息,请参阅 Windows 上的 vsql 用法

1.2 - Windows 上的 vsql 用法

字体

默认光栅字体无法正常与 ANSI 代码页配合工作。请将控制台字体设置为“Lucida Console”。

控制台编码

vsql 构建为“控制台应用程序”。Windows 控制台窗口使用与其他系统不同的编码,因此请谨慎在 vsql 中使用 8 位字符。如果 vsql 检测到有问题的控制台代码页,它会向您发出警告。

若要更改控制台代码页,请输入 cmd.exe /c chcp 1252 以设置代码页。

在 cygwin 下运行

验证 cygwin.bat 文件是否不包含“tty”标记。如果 cygwin.bat 文件包含“tty”标记,则 vsql 中会显示横幅和提示。

若要进行验证,请输入以下命令:

set CYGWIN=binmode tty ntsec

若要移除“tty”标记,请输入以下命令:

set CYGWIN=binmode ntsec

此外,在 Cygwin 下运行时,vsql 使用与 Windows 控制台约定相反的 Cygwin shell 约定。

Tab 自动补全

Tab 自动补全是 shell 的一项功能,而非 vsql 的一项功能。因此,Tab 自动补全在 Windows vsql 中的工作方式与在 Linux 版本的 vsql 中不同。

在 Windows 上,按 F7 可弹出命令的历史记录窗口,而非使用 Tab 自动补全。您也可以在键入命令的几个字母后按 F8,以在历史记录缓冲区中以相同字母开头的命令中循环。

1.3 - 从管理工具进行连接

可以使用 管理工具从群集中的任何节点通过 vsql 连接到数据库。

  1. 以数据库管理员用户身份登录;例如 dbadmin。

  2. 运行管理工具。

    /opt/vertica/bin/admintools
  3. 从主菜单中,选择连接到数据库 (Connect to Database)

  4. 提供数据库密码(如果要求):

    Password:
    

    在使用 CREATE USER 命令创建新用户时,可以配置密码,也可以将其留空。如果在创建用户时配置了密码,则无法绕开密码。可以使用 ALTER USER 命令更改用户的密码。

  5. 管理工具连接到数据库,并将控制权移交给 vsql

    Welcome to vsql, the Vertica Analytic Database interactive terminal.
    Type:  \h or \? for help with vsql commands
           \g or terminate with semicolon to execute query
           \q to quit
    
    =>
    

1.4 - 从命令行进行连接

可以在多个客户端平台上使用 vsql 从命令行连接到数据库。

如果由于任何原因无法建立连接(例如您没有足够的权限或者服务器未在目标主机上运行),则 vsql 会返回错误并终止。

语法

/opt/vertica/bin/vsql [-h host] [ -p port ] [ option...] [ dbname [ username ] ]

参数

host
如果要连接到本地服务器,则此参数是可选的。可以提供 IPv4 或 IPv6 IP 地址,或者也可以提供主机名。

如果 Vertica 服务器同时使用 IPv4 和 IPv6 地址,并且您提供了主机名而非 IP 地址,则当 DNS 已配置为同时提供 IPv4 和 IPv6 地址时,您可以选择将 IPv4 地址与 -4 选项结合使用,还可以选择将 IPv6 地址与 -6 选项结合使用。如果您使用 IPv6 并提供 IP 地址,则必须在地址后面附加 %interface name

port
数据库服务器端口。

默认值: 5433

选项
一个或多个 vsql 命令行选项

如果数据库受密码保护,则您必须指定 -w 或 --password 命令行选项。

dbname
目标数据库的名称。如果未指定,vsql 会自动连接到指定的 hostport 上的数据库。
username
数据库用户名,默认情况下是您的系统用户名。

退出代码

vsql 在正常终止时返回 0 给 shell。否则,它返回以下值之一:

  • 1:发生严重错误 — 例如,内存不足或找不到文件

  • 2:与服务器的连接不正常,并且会话未进行交互

  • 3:脚本中发生了错误,并且设置了变量 ON_ERROR_STOP

  • 命令行中无法识别的单词可能会解释为数据库名称或用户名。

示例

以下示例显示如何通过将 vsql 输出重定向到输出文件 retail_queries.out 来捕获错误消息:

$ vsql --echo-all < retail_queries.sql > retail_queries.out 2>&1

1.4.1 - 命令行选项

本节包含适用于 vsql 的命令行选项。

常规选项

‑‑command command
‑c command
运行一个命令,然后退出。此命令在 shell 脚本中很有用。
‑‑dbname {{< codevar >}}dbname{{< /codevar >}}
‑d {{< codevar >}}dbname{{< /codevar >}}
指定要连接到的数据库的名称。此命令的作用等于将 dbname 指定为命令行中的第一个非选项实参。
‑‑file filename
‑f filename
使用文件 filename 作为命令的源,而不以交互方式读取命令。处理完文件之后,vsql 将终止。
‑‑help
显示有关 vsql 命令行实参的帮助并退出。
‑‑timing
‑i
启用 \timing 元命令。
‑‑list
‑l
返回所有可用数据库并退出。忽略其他非连接选项。此命令类似于内部命令 \list
‑‑set assignment
‑‑variable assignment
‑v assignment
执行变量分配,类似于 vsql 命令 \set
‑‑version ‑V
输出 vsql 版本并退出。
‑‑no‑vsqlr
‑X
禁用所有命令行编辑和历史记录功能。

连接选项

‑4
在双堆栈环境中解析主机名时,首选 IPv4 地址。
‑6
在双堆栈环境中解析主机名时,首选 IPv6 地址。
‑B server:port[,...]
设置连接的备份服务器/端口。使用以逗号分隔的多个主机(默认设置:未设置)。如果使用 IPv6 地址,请用方括号([ 和 ])将地址括起来,并将端口放在方括号之外。例如 \B [2620:0:a13:8a4:9d9f:e0e3:1181:7f51]:5433
‑‑enable‑connection
‑load‑balance ‑C
启用连接负载均衡(默认设置:未启用)。
‑‑host hostname
‑h hostname
指定了服务器正在其上面运行的计算机的主机名。
‑k krb‑service
提供 Kerberos 主体的服务名称部分(默认设置:vertica)。-k 的作用等于使用驱动程序的 KerberosServiceName 连接字符串。
‑K krb‑host
提供 Kerberos 主体的实例或主机名部分。-K 的作用等于驱动程序的 KerberosHostName 连接字符串。
‑‑sslmode
‑m
指定用于建立与服务器的 SSL 连接的策略。选项为 require、prefer、allow 和 disable。设置 VSQL_SSLMODE 变量也可以达到同样效果。如果该变量已设置,命令行选项会覆盖它。
‑‑port {{< codevar >}}port{{< /codevar >}}
‑p {{< codevar >}}port{{< /codevar >}}
指定服务器用来侦听连接的 TCP 端口或本地套接字文件扩展名。默认使用端口 5433。
‑‑username username
‑U username
username 用户身份而非默认用户身份连接到数据库。
‑w password
指定数据库用户的密码。
‑‑password
‑W
强制 vsql 在连接到数据库之前提示输入密码。密码不会显示在屏幕上。即使使用元命令 \connect 更改数据库连接,仍会为整个会话设置此选项。

输出格式

‑‑no‑align
‑A
切换到未对齐输出模式。(默认输出模式为对齐模式。)
‑b
在命令完成后发出嘟声。
‑‑field‑separator separator
‑F separator
指定未对齐输出的字段分隔符(默认分隔符:"|")(‑P fieldsep=)。(请参阅 ‑A ‑‑no‑align。)此命令的作用等于 \pset fieldsep\f
‑‑html
‑H
打开 HTML 表格格式输出。此命令的作用等于 \pset format html\H 命令。
‑‑pset {{< codevar >}}assignment{{< /codevar >}}
‑P {{< codevar >}}assignment{{< /codevar >}}
可用于在命令行中以 \pset 样式指定输出选项。必须用等号 (=) 而非空格来分隔名称和值。因此,若要将输出格式设置为 LaTeX,您应编写 ‑P format=latex
‑Q
打开尾随记录分隔符。使用 \pset trailingrecordsep 可以开关尾随记录分隔符。
‑‑record‑separator {{< codevar >}}separator{{< /codevar >}}
‑R {{< codevar >}}separator{{< /codevar >}}
使用 separator 作为记录分隔符。此命令的作用等于 \pset recordsep 命令。
‑‑tuples‑only
‑t
禁用列名称和结果行计数表尾等的输出。它的作用等于 vsql 元命令 \t
‑‑table‑attr {{< codevar >}}options{{< /codevar >}}
‑T {{< codevar >}}options{{< /codevar >}}
可用于指定要放置在 HTML table 标记中的选项。有关详细信息,请参阅 \pset
‑‑expanded
‑x
启用扩展表格式设置模式。它的作用等于 vsql 元命令 \x

输入选项和输出选项

‑‑echo‑all
‑a
在读取输入行时将所有输入行输出为标准输出。在进行脚本处理时,此方法比交互模式更有用。此选项的作用等于将变量 ECHO 设置为 all
‑‑echo‑queries
‑e
将发送到服务器的所有 SQL 命令复制到标准输出。此命令的作用等于将变量 ECHO 设置为 queries。
‑E
显示由内部命令生成的查询。
‑n
禁用命令行编辑。
‑‑output {{< codevar >}}filename{{< /codevar >}}
‑o {{< codevar >}}filename{{< /codevar >}}
将所有查询输出写入到 filename。此命令的作用等于使用 vsql 元命令 \o
‑‑quiet
‑q
指定 vsql 以安静模式工作(无信息输出,例如欢迎消息)。此选项可与 -c 选项结合使用。在 vsql 中,设置 QUIET 变量也可以达到相同效果。
‑‑single‑step
‑s
在单步模式下运行以调试脚本。强制要求 vsql 在将每个语句发送到数据库之前发出提示,以便取消执行。
‑‑single‑line ‑S
在单行模式下运行,在此模式下,换行符与分号一样会终止 SQL 命令。

1.4.1.1 - -A --no-align

-A--no‑align 切换到未对齐输出模式。默认输出模式为对齐模式。

1.4.1.2 - -a --echo-all

-a--echo-all 在读取输入行时将所有输入行输出为标准输出。在进行脚本处理时,此选项比交互模式更有用。此选项的作用等于将变量 ECHO 设置为 all

1.4.1.3 - -c --command

-c command\--command command 将运行单个命令并在运行后退出。此命令在 shell 脚本中很有用。

可以使用以下两者之一:

  • 一个命令字符串,该命令字符串可由不包含特定于 vsql 的功能的服务器完全解析。

  • 单个元命令

不能将 SQL 元命令和 vsql 元命令一起使用。但是,可以按如下所示将字符串传送到 vsql:

echo "\\timing\\\\select * from t" | ../Linux64/bin/vsql
                Timing is on.
                i | c | v
                ---+---+---
                (0 rows)

1.4.1.4 - -d --dbname

-d db‑name or \--dbname db‑name 指定要连接的数据库的名称。此选项的作用等于将 db‑name 指定为命令行中的第一个非选项实参。

1.4.1.5 - -E

-E 显示由内部命令生成的查询。

1.4.1.6 - -e --echo-queries

-e \--echo-queries 同时将发送到服务器的所有 SQL 命令复制到标准输出。此选项的作用等于将变量 ECHO 设置为 queries

1.4.1.7 - -F --field-separator

-F separator or \--field-separator separator 指定未对齐输出的字段分隔符(默认分隔符:"|")(-P fieldsep=)。(请参阅 -A \--no-align。)它的作用等于 \pset fieldsep\f

若要将字段分隔符值设置为一个控制字符,请使用 shell 的控制字符转义符号。在 Bash 中,可以使用美元符号 ($) 后跟加单引号的字符串的形式在实参中指定控制字符。此字符串可以包含 C 字符串转义(例如 \t 表示制表符)或反斜线 (\),后跟要使用的字符的八进制值。

以下示例演示了如何将分隔符字符设置为制表符 (\t)、垂直制表符 (\v) 以及垂直制表符的八进制值 (\013)。

$ vsql -At -c "SELECT * FROM testtable;"
A|1|2|3
B|4|5|6

$ vsql -F $'\t' -At -c "SELECT * FROM testtable;"
A       1       2       3
B       4       5       6

$ vsql -F $'\v' -At -c "SELECT * FROM testtable;"
A
 1
  2
   3
B
 4
  5
   6
$ vsql -F $'\013' -At -c "SELECT * FROM testtable;"
A
 1
  2
   3
B
 4
  5
   6

1.4.1.8 - -f --file

-f filename\--file filename 使用 filename 作为命令的源,而不以交互方式读取命令。处理完文件之后,vsql 将终止。

如果 filename 是连字符 (-),则会读取标准输入。

使用此选项与编写 vsql < filename 不同。使用 -f 可启用部分附加功能(例如,带行号的错误消息)。相反,使用 shell 的输入重定向的变体始终应生成与您在手动输入所有内容时已获取的输出完全相同的输出。

1.4.1.9 - ?--help

-? \--help 显示有关 vsql 命令行实参的帮助并退出。

1.4.1.10 - -H --html

-H \--html 打开 HTML 表格格式输出。此选项的作用等于 \pset format html\H 命令。

1.4.1.11 - -h --host

-h hostname\--host hostname 指定了服务器正在其上面运行的计算机的主机名。使用此标志远程连接到 Vertica。

具有以下要求和限制:

  • 如果您将客户端身份验证与 gsskrb5 的 Kerberos 连接方法结合使用,则必须指定 -h hostname

  • 如果要从本地连接连接到 Vertica,但要将身份验证记录访问方法 HOST(而不是 LOCAL)结合使用,请使用 -h 选项。

1.4.1.12 - -i -- timing

启用 \timing 元命令。您只能将此命令与 -c --command-f --file 命令一起使用:

$VSQL -h host1 -U user1 -d VMart -p 15 -w ****** -i -f transactions.sql

您只能将 -i-c(命令)和 -f(文件名)命令一起使用。有关详细信息,请参阅命令行选项

从命令行输入 -i 选项,然后运行会话以打开计时。例如:

$VSQL -h host1 -U user1 -d VMart -p 15 -w ****** -i -f transactions.sql
$VSQL-h host1 -U user1 -d VMart -p 15 -w ****** -i -c "SELECT user_name,
ssl_state, authentication_method, client_authentication_name, client_type FROM sessions
WHERE session_id=(SELECT session_id FROM current_session);"

1.4.1.13 - -l --list

-l--list 返回所有可用数据库并退出。忽略其他非连接选项。此命令类似于内部命令 \list

1.4.1.14 - -m --sslmode

-m--sslmode 指定用于建立与服务器的 SSL 连接的策略。选项为 verify_fullverify_ca requirepreferallowdisable。设置 VSQL_SSLMODE 变量也可以达到同样效果。如果该变量已设置,命令行选项会覆盖它。

有关这些模式的信息,请参阅 为 ODBC 客户端配置 SSL。

1.4.1.15 - -n

-n 禁用命令行编辑。

1.4.1.16 - -o --output

-o filename\--output filename 将所有查询输出写入 filename。它的作用等于 vsql 元命令 \o

1.4.1.17 - -P --pset

-P assignment\--pset assignment 可用于在命令行中以 \pset 样式指定输出选项。请注意,必须用等号而非空格来分隔名称和值。因此,若要将输出格式设置为 LaTeX,您应编写 -P format=latex

1.4.1.18 - -p --port

-p port\--port port 指定服务器用来侦听连接的 TCP 端口或本地套接字文件扩展名。默认使用端口 5433。

1.4.1.19 - -q --quiet

-q--quiet 指定 vsql 以安静模式工作。默认情况下,它会输出欢迎消息和各种信息输出。如果使用此选项,则这些信息均不显示。此选项可与 -c 选项结合使用。在 vsql 中,设置 QUIET 变量也可以达到相同效果。

1.4.1.20 - -R --record-separator

-R separator\--record-separator separator 指定将 separator 作为记录分隔符。它的作用等于 \pset recordsep 命令。

1.4.1.21 - -S --single-line

-S \--single-line 在单行模式下运行,在此模式下,换行符与分号一样会终止 SQL 命令。

1.4.1.22 - -s --single-step

-s \--single-step 在单步模式下运行以调试脚本。强制要求 vsql 在将每个语句发送到数据库之前发出提示,以便取消执行。

1.4.1.23 - -T --table-attr

-T table‑options\--table-attr table‑options 允许您指定要放置在 HTML table 标记中的选项。有关详细信息,请参阅 \pset

1.4.1.24 - -t --tuples-only

-t--tuples-only 禁用列名称和结果行计数表尾等的输出。它的作用等于 vsql 元命令 \t

1.4.1.25 - -V --version

-V--version 输出 vsql 版本并退出。

1.4.1.26 - -v --variable --set

-v assignment\--variable assignment\--set assignment 指定变量分配,类似于 vsql 元命令 \set

若要取消设置变量,请省略等号。若要设置不具有值的变量,请使用等号而省略值。请在早期启动阶段执行这些分配,以便可以在稍后覆盖预留用于内部用途的变量。

1.4.1.27 - -X --no-vsqlrc

-X \--no-vsqlrc 防止读取启动文件(系统范围 vsqlrc 文件或用户的 ~/.vsqlrc 文件)。

1.4.1.28 - -x --expanded

-x--expanded 启用扩展表格式设置模式。它的作用等于 vsql 元命令 \x

1.4.2 - 从非群集主机进行连接

在非群集 Linux 主机上,您可以使用 Vertica vsql 可执行映像连接到 Vertica 数据库。

  • 在 Red Hat、CentOS 和 SUSE 系统上,您可以安装包括 vsql 可执行文件的客户端驱动程序 RPM。有关详细信息,请参阅安装 vsql 客户端

  • 如果非群集主机与群集主机运行的 Linux 版本相同,请将映像文件复制到远程系统。例如:

    $ scp host01:/opt/vertica/bin/vsql .$ ./vsql
    
  • 如果非群集主机运行的 Linux 发行版或版本与群集主机不同,则必须安装 Vertica 服务器 RPM 才能获取 vsql:

    1. 通过浏览到 Vertica 网站下载相应的 RPM 包。在支持 (Support) 选项卡上,选择客户下载 (Customer Downloads)

    2. 如果您下载 RPM 所用的系统不是非群集主机,请将文件传输到非群集主机。

    3. 以 root 身份登录到非群集主机,并使用以下命令安装 RPM 包:

      # rpm -Uvh filename
      

      其中,*filename * 是所下载的包。请注意,要使用 vsql,您无需在非群集主机上运行 install_vertica 脚本。

  • 使用与群集主机相同的命令行选项

  • 不能在 Cygwin bash shell (Windows) 上运行 vsql。请使用 ssh 连接到群集主机,然后再运行 vsql。

1.5 - 元命令

在 vsql 中输入的任何以不带引号的反斜杠开头的命令称为 vsql 元命令,这种命令由 vsql 本身进行处理。这些命令可使 vsql 更有助于进行管理或脚本编写。元命令的更常用叫法是斜杠命令或反斜杠命令。

vsql 命令的格式是反斜杠的后面依次紧跟命令动词和任何参数。参数和命令动词用任意数量的空格字符分隔,并且各个参数用任意数量的空格字符分隔。

若要在参数中包含空格,您可以用单引号将该参数括起来。若要在参数中包含单引号,请在该参数前面附加反斜杠。此外,包含在单引号中的任何内容需要进行类 C 替换,以替代 \n(换行符)、\t(制表符)、\\数字\0数字\0x数字(带有给定十进制、八进制或十六进制代码的字符)。

如果不带引号的参数以冒号 (:) 开头,则该参数会被视为 vsql 变量,并且变量的值会改为用作参数。

用反引号 (```) 括起来的实参会被视为一个命令行并传递到 shell。命令的输出(移除了任何尾随换行符)会被视为参数值。以上转义序列在反引号中也适用。

有些命令使用 SQL 标识符(例如表名称)作为参数。这些实参遵循 SQL 的语法规则:不带引号的字母会强制转换为小写,而双引号 (") 可防止对字母进行大小写转换并允许在标识符中包含空格。在双引号中,成对的双引号在生成的名称中会减少为单个双引号。例如,FOO"BAR"BAZ 会解释为 fooBARbaz,而 "A weird"" name" 将变为 A weird" name

当出现其他不带引号的反斜杠时,对参数进行的解析将停止。此不带引号的反斜杠会被视为新的元命令的开头。特殊序列 \\(两个反斜杠)标记参数的结尾,并继续解析 SQL 命令(如果有)。这样,您可以随意地在单个行中将 SQL 命令和 vsql 命令一起使用。但在任何情况下,元命令的参数都不能超过行的结尾。

1.5.1 - 元命令快速参考

1.5.2 - \connect

以指定用户 user‑name 身份建立与数据库 db 的连接。上一个连接将关闭。如果您不指定数据库名称,Vertica 将连接到当前数据库。如果您不指定用户名实参,Vertica 将假定为当前用户。

语法

\c[connect] [db [user‑name]]

错误处理

阻止执行的错误包括:指定未知用户和拒绝访问指定数据库。Vertica 以不同方式处理错误,具体取决于此命令是在 vsql 中还是在脚本中交互执行:

  • VSQL 处理:当前连接保持不变。

  • 脚本:处理立即停止并显示错误。这可以防止脚本作用于错误的数据库。

1.5.3 - \d 元命令

Vertica 支持许多 \d 命令,这些命令返回有关不同类别的数据库对象的信息。有关完整列表,请参阅下面的 \d 参考

语法

除非另有说明,否则 \d 命令通常遵循以下语法:

\dCommand [ [schema.]pattern ]

参数

您可以为大多数 \d 命令提供字符串模式实参,用于筛选命令返回的结果。该模式可以选择由架构名称限定。

架构
对大多数 \d 命令有效,将输出仅限制为 schema 中的数据库对象。例如,以下 \dp 命令获取包含字符串 resource 的所有 V_MONITOR 表的权限信息:
=> \dp V_MONITOR.*resource*
                Access privileges for database "dbadmin"
 Grantee | Grantor | Privileges |  Schema   |            Name
---------+---------+------------+-----------+----------------------------
 public  | dbadmin | SELECT     | v_monitor | resource_rejections
 public  | dbadmin | SELECT     | v_monitor | disk_resource_rejections
 public  | dbadmin | SELECT     | v_monitor | resource_usage
 public  | dbadmin | SELECT     | v_monitor | resource_acquisitions
 public  | dbadmin | SELECT     | v_monitor | resource_rejection_details
 public  | dbadmin | SELECT     | v_monitor | resource_pool_move
 public  | dbadmin | SELECT     | v_monitor | host_resources
 public  | dbadmin | SELECT     | v_monitor | node_resources
 public  | dbadmin | SELECT     | v_monitor | resource_queues
 public  | dbadmin | SELECT     | v_monitor | resource_pool_status
(10 rows)
  
模式
仅返回与指定字符串匹配的数据库对象。模式字符串可以包含以下通配符:
  • * (星号):零个或多个字符。

  • ? (问号):任何单个字符。

例如,以下 \dt 命令返回以字符串 store 开头的表:

=> \dt store*
                     List of tables
 Schema |       Name        | Kind  |  Owner  | Comment
--------+-------------------+-------+---------+---------
 public | store_orders      | table | dbadmin |
 public | store_orders_2018 | table | dbadmin |
 public | store_overseas    | table | dbadmin |
 store  | store_dimension   | table | dbadmin |
 store  | store_orders_fact | table | dbadmin |
 store  | store_sales_fact  | table | dbadmin |
(6 rows)
  

\d 参考

\d
如果不由模式实参限定,则返回所有表及其架构名称、所有者和注释。如果由模式实参限定,则 \d 返回所有匹配的表和每个表中的所有列,以及每个列的详细信息,例如数据类型、大小和默认值。
\df
返回所有函数名称、函数返回数据类型和函数实参数据类型。此元命令还会返回用户可用的所有过程的过程名称和参数。
\dj
返回所有投影,并显示架构、投影名称、所有者和节点。返回的行包括超投影、实时聚合投影、Top-K 投影和带表达式的投影。
\dn
返回架构名称和架构所有者。
\dp
返回系统表 V_CATALOG.GRANTS 中所有对象的权限摘要:被授予者、授予者、权限、架构和对象名称(相当于 \z)。
\dS
如果不由模式实参限定,则返回所有 V_CATALOGV_MONITOR 系统表。要仅获取一个架构的系统表,请使用架构名称限定命令,如下所示:
\dS { V_CATALOG | V_MONITOR }.*
\ds
返回序列及其参数。
\dT
返回 Vertica 支持的所有数据类型。
\dt
如果不由模式实参限定,则返回与未限定 \d 命令相同的信息。如果由模式实参限定,则 \dt 返回与未限定 \dt 命令具有相同详细级别的匹配表。
\dtv
返回表和视图。
\du
返回数据库用户以及他们是否是超级用户。
\dv
如果不由模式实参限定,则返回所有视图及其架构名称、所有者和注释。如果由模式实参限定,\dv 返回所有匹配的视图和每个视图中的列,以及每列的数据类型和大小。

1.5.4 - \edit

使用外部编辑器编辑查询缓冲区(或指定的文件)。当编辑器退出时,其内容会复制回查询缓冲区。如果未指定任何参数,则当前的查询缓冲区会复制到临时文件,然后以相同方式编辑该临时文件。

然后,将按照 vsql 的一般规则重新解析新的查询缓冲区,按照这些规则,包括第一个分号在内的整个缓冲区会被视为单个行。(因此,您无法使脚本以这种方式工作。请使用 \i 获取该结果。)如果没有任何分号,vsql 将等待用户输入分号(它不会执行查询缓冲区)。

语法

\e[dit] [ file ]

1.5.5 - \i

R从指定的文件读取并执行输入。

语法

\i filename

示例

Linux 上的 Vertica vsql 客户端支持反引号(反撇号)扩展。例如:

  1. 将环境变量设置为包含要运行的脚本的路径:

    $ export MYSCRIPTS=/home/dbadmin/testscripts
    
  2. 发出 vsql 命令。

    $ vsql
    
  3. 使用反引号扩展来包含运行现有脚本的路径,例如 sample.sql

    => \i `echo $MYSCRIPTS/sample.sql`
    

1.5.6 - \locale

显示或设置当前会话的区域设置。

语法

\locale [locale-identifier]

参数

locale-identifier
指定要使用的 ICU 区域设置标识符,默认设置为:
en_US@collation=binary

如果设置为空字符串,Vertica 会将区域设置设置为 en_US_POSIX

如果省略此实参,则 \locale 返回当前区域设置。

有关标识符选项的详细信息,请参阅关于区域设置。有关区域设置标识符的完整列表,请参阅 ICU 项目

示例

查看当前的区域设置:

=> \locale
en_US@collation=binary

更改此会话的默认区域设置:

=> \locale en_GBINFO:
INFO 2567:  Canonical locale: 'en_GBINFO:'
Standard collation: 'LEN'
English (GBINFO:)

注意

服务器区域设置仅影响服务器端查询处理的排序行为。客户端应用程序负责确保所设置的区域设置正确无误,以便正确显示字符。以下是 Vertica 推荐的最佳实践,可确保结果可预测:

  • 应将 vsql 的终端模拟器 (POSIX) 中的区域设置设置为与服务器端 (ICU) 上设置的会话区域设置相同,以便在服务器上正确整理数据并在客户端上正确显示数据。

  • 应在终端模拟器中使用 POSIX LANG 环境变量来设置 vsql 区域设置。有关如何设置区域设置,请参阅终端模拟器的文档。

  • 服务器会话区域设置应使用 为数据库指定默认区域设置 中所述的设置进行设置。

  • vsql 的所有输入数据应该为 UTF-8,而所有输出数据都以 UTF-8 进行编码。

  • 不支持非 UTF-8 编码和关联的区域设置值。

1.5.7 - \pset

设置一些选项来控制 Vertica 如何设置查询结果输出格式。

语法

\pset output-option

输出选项

format format‑option
设置输出格式,其中 format‑option 是以下项之一:
  • u[naligned] 将每一行的所有列数据写入一行,其中每个字段仅由当前分隔符分隔。将此输出用作其他程序的输入,例如用于 CSV 输入的逗号分隔字段。

  • a[ligned] (默认值):呈现按列对齐的输出。

  • h[tml]:将 HTML 标记中的输出呈现为表。

  • l[atex]:在 LaTex 标记中呈现输出。

border int
仅当输出格式设置为 html 时有效,指定表边框,其中 int 指定边框类型。
expanded
在常规格式和扩展格式之间切换。如果启用了扩展格式,则所有输出都包含两个列,并且列名称位于左列而数据位于右列。这种模式对于宽表尤其有用。
fieldsep 'arg'
仅当输出格式设置为 unaligned 时有效,指定字段分隔符,默认分隔符为 |(竖线)。

例如,要将 Tab 指定为字段分隔符:

\pset fieldsep '\t'
footer
在是否显示默认表尾之间切换: int 行)
null 'string'
指定此项可将列 null 值表示为 string。默认情况下,Vertica 将 null 值呈现为空字段,这可能会被误认为是空字符串。

例如:

\pset null '(null)'
pager [always]
在针对查询和 vsql 帮助输出是否使用寻呼机之间切换。如果已设置环境变量 PAGER,则输出会传送到指定的程序。否则会使用由平台决定的默认设置(例如 more)。

如果寻呼机已关闭,则不会使用寻呼机。如果寻呼机已打开,则只会在适当时使用寻呼机;也就是说,输出将传送到终端,并且不会显示在屏幕上。(vsql 无法准确评估何时应使用寻呼机。)

如果始终使用该实参进行限定,则始终使用寻呼机。

recordsep 'char'
仅当输出格式设置为 unaligned 时有效,指定用于分隔表记录(元组)的字符,默认分隔符为换行符。
tableattr html‑attribute[...]
指定要放在 HTML table 标记中的属性,例如 cellpaddingbgcolor
title ['title‑str']
将位于查询结果输出之前的标题设置为 title‑str。HTML 输出呈现如下:
<caption>title-str</caption>

要移除标题,请重新发出命令,省略 title‑str 实参。

trailingrecordsep
开关要在未对齐输出模式中使用的尾随记录分隔符。
t[uples_only]
在仅显示元组和完整显示之间切换。完整显示可能会显示额外信息,例如,列标题、表标题和各个表尾。在仅显示元组模式下,只会显示实际表数据。

快捷方式

以下 \pset 命令有快捷方式:

\pset expanded
\x
\pset fieldsep 'arg'
\f
\pset format aligned
\a
\pset format html
\H
\pset tableattr html‑attribute[...]
\T html‑attribute[...]
\pset title title-str
\C ['title-str']
\pset tuples_only
\t

示例

请参阅输出格式设置示例

1.5.8 - \set

S将内部变量设置为一个或多个值。如果指定了多个值,则将它们串连起来。未限定的 \set 命令将列出所有内部变量。

要取消设置变量,请使用 vsql 元命令 \unset

语法

\set [var [value]...]

参数

var
要设置的内部变量的名称。有效的变量名区分大小写,可以包含字符、数字和下划线。vsql 将几个变量视为特殊变量,如变量中所述。
value
要在变量 var 中设置的值。如果未指定值,则变量设置为无值。

如果设置为空字符串,则变量设置为无值。如果省略此实参,则 \set 返回所有内部变量。

如果未提供任何实参,则 \set 返回所有内部变量。例如:

=> \set
VERSION = 'vsql'
AUTOCOMMIT = 'off'
VERBOSITY = 'default'
PROMPT1 = '%/%R%# '
PROMPT2 = '%/%R%# '
PROMPT3 = '>> '
ROWS_AT_A_TIME = '1000'
DBNAME = 'dbadmin'
USER = 'dbadmin'
PORT = '5433'
LOCALE = 'en_US@collation=binary'
HISTSIZE = '500'

1.5.9 - \timing

如果设置为 on,则返回每个 SQL 语句运行的时间长度(以毫秒为单位)。结果包括:

  • 获取第一个行块所需的时间长度

  • 直到最后一个块格式化的总时间。

未限定的 \timing 在打开和关闭计时之间切换。您可以通过分别使用选项 ONOFF 限定命令来显式打开和关闭计时。

语法

\timing [ON | OFF]

示例

以下未限定的 \timing 命令在打开和关闭计时之间切换:

=> \timing
Timing is on
=> \timing
Timing is off

以下示例显示了一个打开计时的 SQL 命令:

=> \timing
Timing is on.
=> SELECT user_name, ssl_state, authentication_method, client_authentication_name,
     client_type FROM sessions WHERE session_id=(SELECT session_id FROM current_session);
 user_name | ssl_state | authentication_method | client_authentication_name | client_type
-----------+-----------+-----------------------+----------------------------+-------------
 dbadmin   | None      | ImpTrust              | default: Implicit Trust    | vsql
(1 row)

Time: First fetch (1 row): 73.684 ms. All rows formatted: 73.770 ms

1.6 - 变量

vsql 提供与常用 Linux 命令 shell 相似的变量替换功能。变量是名称/值对,其中值可以是任意长度的字符串。若要设置变量,请使用 vsql 元命令 \set:例如,以下语句将变量 fact 设置为值 dim

=> \set fact dim

如果您对变量调用 \set 并且不提供任何值,则该变量设置为空字符串。

获取变量

若要检索给定变量的内容,请在名称前面附加冒号,并将此用作斜杠命令的实参:例如:

=> \echo :fact
dim

未限定的 \set 命令会返回所有当前变量及其值:

dbadmin=> \set
VERSION = 'vsql'
AUTOCOMMIT = 'off'
VERBOSITY = 'default'
PROMPT1 = '%/%R%# '
PROMPT2 = '%/%R%# '
PROMPT3 = '>> '
ROWS_AT_A_TIME = '1000'
DBNAME = 'dbadmin'
USER = 'dbadmin'
PORT = '5433'
LOCALE = 'en_US@collation=binary'
HISTSIZE = '500'

删除变量

若要取消设置(删除)变量,请使用 vsql 元命令 \unset

变量命名约定

vsql 内部变量名称可以包含任意数量的字母、数字和下划线(而且它们可以按任何顺序出现)。vsql 会对某些内部变量进行特殊处理。这些变量指示可以在运行时通过更改变量的值来更改某些选项设置,或者表示应用程序的某种状态。虽然可以将这些变量用于任何其他用途,但建议您不要这样做。按照约定,所有特殊处理的变量均包含全大写字母(并且可能包含数字和下划线)。若要确保在将来具有最高兼容性,请避免将这些变量名称用于您自己的用途。

SQL 插值

您可以将 vsql 变量替换(“插值”)到常规 SQL 语句中。为此,可以在变量名称前加上冒号 (:)。例如,以下语句查询表 my_table

=> \set fact 'my_table'
=> SELECT * FROM :fact;

将逐字复制变量的值,因此,变量甚至可以包含不对称的引号或包含反斜杠命令。请确保将变量放置到能够起作用的位置。不会执行到带有引号的 SQL 实体的变量插值。存在一个例外:带有反引号 (````) 的字符串的内容会传递到系统 shell,并替换为 shell 的输出。请参阅下面的“使用反引号读取系统变量”。

使用反引号读取系统变量

在 vsql 中,反引号的内容会传递到系统 shell 进行解释(此行为与许多 UNIX shell 相同)。在设置 vsql 内部变量时,此行为特别有用,因为您可能需要访问 UNIX 系统变量(例如 HOME 或 TMPDIR)而非硬编码值。

例如,要将内部变量设置为 UNIX 用户目录中某个文件的完整路径,您可以使用反引号获取 HOME 系统变量(此变量是指向用户目录的完整路径)的内容:

=> \set inputfile `echo $HOME`/myinput.txt=> \echo :inputfile
/home/dbadmin/myinput.txt

反引号之内的内容会替换为在系统 shell 解释器中运行这些内容后的结果。在此示例中,echo $HOME 命令将返回 HOME 系统变量的内容。

1.6.1 - AUTOCOMMIT

当 AUTOCOMMIT 设置为“on”时,每个 SQL 命令会在成功完成后自动提交;例如:

\set AUTOCOMMIT on

若要在此模式下推迟 COMMIT,请将值设置为 off。

\set AUTOCOMMIT off

如果 AUTOCOMMIT 为空或定义为 off,则 SQL 命令不会提交,除非您显式发出 COMMIT。

注意

  • 默认情况下,AUTOCOMMIT 设置为 off。

  • AUTOCOMMIT 必须为大写,但其值(on 或 off)不区分大小写。

  • 在关闭自动提交模式下,您必须通过输入 ABORT 或 ROLLBACK 来显式放弃任何失败的事务。

  • 如果在未提交的情况下退出会话,将回退您所做的工作。

  • 对 vsql 变量的验证在运行这些变量时进行,而不在设置这些变量时进行。

  • 默认情况下,COPY 语句会在完成后提交,因此使用哪种 AUTOCOMMIT 模式并没什么影响,除非您发出 COPY NO COMMIT。请注意,DDL 语句会自动提交。

  • 若要确定 AUTOCOMMIT 设置为 on 还是 off,请发出以下 set 命令:

    $ \set...
    AUTOCOMMIT = 'off'
    ...
    
  • 如果 SELECT * FROM LOCKS 显示了来自刚才运行的语句的锁,则表示 AUTOCOMMIT 设置为 off。

    $ \set AUTOCOMMIT off
    $ \set
    ...
    AUTOCOMMIT = 'off'
    ...
    SELECT COUNT(*) FROM customer_dimension;
     count
    -------
     50000
    (1 row)
    SELECT node_names, object_name, lock_mode, lock_scope
    FROM LOCKS;
     node_names |      object_name         | lock_mode | lock_scope
    ------------+--------------------------+-----------+-------------
     site01     | Table:customer_dimension | S         | TRANSACTION
    (1 row)
    

1.6.2 - DBNAME

当前已连接到的数据库的名称。每次连接到数据库(包括程序启动)时都会设置 DBNAME,但也可以取消设置它。

1.6.3 - ECHO

如果设置为 all,则通过键盘或脚本输入的所有行会写入到标准输出,然后再解析或运行这些行。

若要在程序启动时选择此行为,请使用 -a 开关。如果设置为 queries,则 vsql 只会按照发送到服务器的内容输出所有查询。此行为的开关是 -e

1.6.4 - ECHO_HIDDEN

如果设置此变量,则当反斜杠命令查询数据库时,首先会显示查询。这样,您可以学习 Vertica 内部命令并在您自己的程序中提供相似功能。(若要在程序启动时选择此行为,请使用 -E 开关。)

如果将此变量设置为值 noexec,则只会显示查询,而实际上不会将查询发送到服务器,也不会运行查询。

1.6.5 - ENCODING

当前的客户端字符集编码。

1.6.6 - HISTCONTROL

如果此变量设置为 ignorespace,则以空格开头的行不会输入到历史记录列表中。如果此变量设置为值 ignoredups,则不会输入与上一个历史记录行匹配的行。值 ignoreboth 结合了两个选项。如果取消设置此变量,或者将此变量设置为前述值以外的任何其他值,则在交互模式下读取的所有行都会保存到历史记录列表中。

来源: Bash。

1.6.7 - HISTSIZE

设置用于存储命令历史记录的空间量。该值大致近似于 vsql 在其命令历史记录缓冲区中存储的命令数。此值仅影响当前 vsql 会话存储的行数。它不影响 .vsql_history 文件中存储的历史记录。

默认值为 500。

来源: Bash。

1.6.8 - 主机

当前连接到的数据库服务器主机。每次连接到数据库(包括程序启动)时都会设置它,但也可以取消该设置。

1.6.9 - IGNOREEOF

如果取消设置此变量,则向 vsql 的交互会话发送 EOF 字符(通常是按 Ctrl+D)会终止应用程序。如果将此变量设置为数值,则应用程序在终止之前会忽略许多 EOF 字符。如果设置了此变量但未提供任何数值,则默认值为 10。

来源: Bash。

1.6.10 - ON_ERROR_STOP

默认情况下,如果脚本命令生成错误(例如,由于命令格式不正确或数据格式无效),处理将继续进行。如果在脚本中将 ON_ERROR_STOP 设置为 ON,并且在处理期间发生错误,脚本将立即终止。

例如:

=> \set ON_ERROR_STOP ON

1.6.11 - PORT

当前连接到的数据库服务器端口。每次连接到数据库(包括程序启动)时都会设置此变量,但也可以取消设置此变量。

1.6.12 - PROMPT1 PROMPT2 PROMPT3

这些变量指定 vsql 所发出的提示的内容。有关详细信息,请参阅提示

1.6.13 - QUIET

此变量相当于命令行选项 -q。此变量在交互模式下可能不太有用。

1.6.14 - ROWS_AT_A_TIME

ROWS_AT_A_TIME 默认设置为 1000,并将结果检索为具有该大小的行块。第一个块的列格式设置用于所有块,因此在稍后的块中,有些条目可能会溢出。

设置结果的格式时,Vertica 会将 ROWS_AT_A_TIME 行缓冲到内存中,以计算最大列宽。如果任何字段值的长度超过前几个 ROWS_AT_A_TIME 行中出现的字段值,则初始提取之后的行可能未正确对齐。 ROWS_AT_A_TIME 可以使用 vsql 元命令 \unset 取消设置以保证正确对齐。不过,这要求将整个结果集重新缓冲到内存中,可能会在结果集太大时导致 vsql 失败。

1.6.15 - SINGLELINE

此变量相当于命令行选项 -S

1.6.16 - SINGLESTEP

此变量相当于命令行选项 -s

1.6.17 - 用户

当前用来进行连接的数据库用户。每次连接到数据库(包括程序启动)时都会设置它,但也可以取消该设置。

1.6.18 - VERBOSITY

此变量可以设置为值 defaultverboseterse,以控制错误报告的详细程度。

1.6.19 - VSQL_HOME

默认情况下,vsql 程序从用户的主目录读取配置文件。如果这么做不合适,可以通过设置 VSQL_HOME 环境变量来覆盖配置文件位置,而且设置 VSQL_HOME 环境变量时可以不需要修改共享资源。

在以下示例中,vsql 从 /tmp/jsmith 而非 ~ 读取配置信息。

# Make an alternate configuration file in /tmp/jsmith
mkdir -p /tmp/jsmith
echo "\\echo Using VSQLRC in tmp/jsmith" > /tmp/jsmith/.vsqlrc
# Note that nothing is echoed when invoked normally
vsql
# Note that the .vsqlrc is read and the following is
# displayed before the vsql prompt
#
# Using VSQLRC in tmp/jsmith
VSQL_HOME=/tmp/jsmith vsql

1.6.20 - VSQL_SSLMODE

VSQL_SSLMODE 指定客户端(例如 admintools)在连接到服务器时如何使用(或是否使用)SSL。默认值为 prefer,表示使用 SSL(如果服务器提供的话)。合法值为 requirepreferallowdisable。此变量的作用等于命令行选项 -m(或 --sslmode)。

1.7 - 提示

可以根据您的首选项自定义 vsql 所发出的提示。PROMPT1PROMPT2PROMPT3 三个变量可以包含描述了提示的外观的字符串和特殊转义序列。提示 1 是在 vsql 请求新的命令时使用的普通提示。如果在输入命令期间由于命令不以分号终止或引号未关闭而需要更多输入,将发出提示 2。当您运行 SQL COPY 命令并且需要在终端上键入行值时,将发出提示 3。

将逐字输入所选提示变量的值,除非遇到百分号 (%)。根据下一个字符,某些其他文本会被替换。已定义的替换如下:

%M
数据库服务器的完整主机名(包括域名)、[local](如果连接通过套接字)或 [local:/dir/name](如果不在默认位置编译该套接字)。
%m
数据库服务器的主机名(在第一个点处截断)或 [local]。
%>
数据库服务器正在侦听的端口号。
%n
数据库会话的用户名。
%/
当前数据库的名称。
%~
类似于 %/,但如果数据库是默认数据库,则输出是 ~(波浪符)。
%#
如果会话用户是数据库超级用户,则输出 #,否则会输出 >。(如果运行 SET SESSION AUTHORIZATION 命令,此值的扩展在数据库期间可能会更改。)
%R
在提示 1 中,通常替换为 =;但如果处于单行模式,则替换为 ^;如果会话已从数据库断开连接(当 \connect 失败时,将出现这种情况),则替换为 !。在提示 2 中,序列会替换为 -、、单引号、双引号或美元符号,具体取决于 vsql 是由于命令尚未终止还是由于处于 / ...*/ 注释之内或处于以引号或美元符号转义的字符串之内而需要更多输入。在提示 3 中,该序列不会生成任何内容。
%x
事务状态:空字符串(如果不在事务块中)、*(如果在事务块中)、!(如果在失败的事务块中)或 ? [如果事务状态不确定(例如,由于无连接)]。
%digits
带有所指示的数字代码的字符会被替换。如果数字以 0x 开头,则其余字符会解释为十六进制;如果第一个数字是 0,则数字会解释为八进制;否则会作为十进制数字读取数字。
%:name:
vsql 变量名称的值。有关详细信息,请参阅“变量”一节。
%`command`
命令的输出,类似于常见的“重音符”替换。
%[ ... %]
提示可能包含终端控制字符,例如,这些控制字符可能更改提示文本的颜色、背景或样式,或者可能更改终端窗口的标题。为使 Readline 的行编辑功能正常工作,必须通过用 %[ 和 %] 将这些非输出控制字符括起来将其指定为不可见。提示中可能会出现多对这些字符。以下示例将在兼容 VT100 且支持彩色的终端上生成粗体 (1;) 的黄色且带有黑色背景 (33;40) 的提示:

testdb=> \set PROMPT1 '%[%033[1;33;40m%]%n@%/%R%#%[%033[0m%] '

若要将百分号插入到提示中,请编写 %%。提示 1 和 2 的默认提示为 '%/%R%#',提示 3 的默认提示为 '>>'。

注意: 请参阅终端控制序列的规范(适用于 gnome-terminal 和 xterm)。

1.8 - 命令行编辑

vsql 支持 tecla 库,以便于进行行编辑和检索。

当 vsql 退出时,将自动保存命令历史记录;当 vsql 启动时,将重新加载命令历史记录。还支持 Tab 自动补全,但完成逻辑不会声称自己是 SQL 解析器。如果您由于某种原因而不想使用 Tab 自动补全,可以通过将以下代码放到主目录中名为 .teclarc 的文件来关闭该功能:

bind ^I

有关更多详细信息,请阅读 tecla 文档。

注意

tecla 库的 vsql 实施与 tecla 文档中所述存在如下偏差:

  • 重新调用先前键入的行

    在纯 tecla 下,所有新行会附加到历史输入行(在 GetLine 资源对象中维护)的列表中。在 vsql 中,只有不同的非空行才会附加到历史输入行的列表中。

  • 历史记录文件

    tecla 的历史记录文件没有标准名称。在 vsql 中,文件名为 ~/.vsql_hist。

  • 国际字符集(元键和区域设置)

    在 vsql 中,不再支持 8 位元字符。请通过将元字符的 EightBitInput X 资源设置为 False 来确保元字符发送转义符。可以通过以下方法之一执行此操作:

    • 通过添加以下行来编辑 ~/.Xdefaults 文件:

      XTerm*EightBitInput: False
      
    • 使用 -xrm“*EightBitInput: False”命令行参数启动 xterm。

  • 键绑定:

  • 以下键绑定特定于 vsql:

    • Insert,在插入模式(默认模式)和覆盖模式之间切换。

    • Delete,删除光标右侧的字符。

    • Home,将光标移到行的开头。

    • End,将光标移到行的结尾。

    • ^R,执行历史记录向后搜索。

1.9 - vsql 环境变量

每次启动 vsql 时,将自动设置下列一个或多个环境变量以供已定义的属性使用:

PAGER
如果查询结果无法在屏幕上显示,将通过此命令传送这些结果。典型值为 more less。默认值由平台决定。使用 \pset 命令启用/禁用寻呼机。
VSQL_DATABASE
您连接的数据库。例如 VMart
TMPDIR
用于存储临时文件的目录。默认值由平台决定。在类 Unix 系统上,默认值为 /tmp
VSQL_EDITOR
EDITOR
VISUAL
由 \e 命令使用的编辑器。将按照列出变量的顺序检查变量;将使用所设置的第一个变量。
VSQL_HOME
默认情况下,vsql 程序从用户的主目录读取配置文件。如果这么做不合适,可以通过设置 VSQL_HOME 环境变量来覆盖配置文件位置,而且设置 VSQL_HOME 环境变量时可以不需要修改共享资源。
VSQL_HOST
Vertica 节点的主机名或 IP 地址。
VSQL_PASSWORD
数据库密码。使用此环境变量可提高站点安全性,因为无需在命令行中输入数据库密码。
VSQL_PORT
要用于连接的端口。
VSQL_SSLMODE
指定客户端(如 admintools)在连接到服务器时是否以及如何使用 SSL。
VSQL_USER
要用于连接的用户名。

1.10 - 区域设置

Linux 下的默认终端模拟器是 gnome-terminal,但您也可以使用 xterm。

Vertica 建议在 UTF-8 模式下将 gnome-terminal 与 vsql 结合使用,这是默认设置。

在 Linux 上更改设置

  1. 从 vsql 屏幕顶部的选项卡中,选择“终端 (Terminal)”。

  2. 单击设置字符编码 (Set Character Encoding)

  3. 选择 Unicode (UTF-8)

在 Windows 上使用 PuTTy 更改设置

  1. 右键单击 vsql 屏幕标题栏,然后选择更改设置 (Change Settings)

  2. 单击窗口 (Window),然后单击转换 (Translation)

  3. 从右侧的下拉菜单中选择 UTF-8

注意

  • vsql 无法了解您如何设置了终端模拟器选项。

  • tecla 库已准备好使用 POSIX LANG 等环境变量对交互式输入执行从本地编码到 UTF-8 的 POSIX 类型转换。此类型转换对使用非 UTF-8 键盘的国际用户很有用。有关详细信息,请参阅 tecla 文档。

    Vertica 建议使用以下区域设置(或您觉得合适的任何其他 .UTF-8 区域设置):

    export LANG=en_US.UTF-8
    
  • vsql \locale 命令将调用并跟踪服务器的 SET LOCALE TO 命令,如中所述。目前,vsql 本身不对此区域设置执行任何操作,而会将其输入(来自文件或 tecla)、所有输出及其与服务器的所有交互视为 UTF-8。除了在 printf 等中使用的任何“automatic”之外,vsql 将忽略 POSIX 区域变量。

1.11 - 使用 vsql 输入数据

使用 vsql 时经常需要插入字面量数据。例如:

  • 使用 INSERT 语句向表中添加一行数据。

  • 通过 COPY FROM STDIN 语句添加多行数据。

下表列出了 Vertica 支持的数据类型,以及您在使用 vsql 时用于在查询中输入该数据的格式。

1.12 - 文件

启动之前,vsql 会尝试读取并执行系统范围 vsqlrc 文件和用户的 ~/.vsqlrc 文件中的命令。命令行历史记录存储在文件 ~/.vsql_history.

1.13 - 使用 vsql 导出数据

可以使用 vsql 执行简单的数据导出任务,方法是更改其输出格式选项,以使输出适用于导入到其他系统(例如,Tab 分隔文件或逗号分隔文件)。可以从 vsql 会话中设置这些选项,或者也可以通过传递到 vsql 命令的命令行参数设置这些选项(这样可使导出过程适合通过编写脚步来实现自动化)。设置了 vsql 选项以使其采用目标系统可读取的格式输出数据之后,您可以运行查询并将结果捕获到文本文件。

下表列出了可用于更改 vsql 的输出格式的元命令和命令行选项。

以下示例演示了在输出中禁用填充和列标题,并演示了在交互式会话中设置字段分隔符以将表转储到制表符分隔的文本文件。

=> SELECT * FROM my_table;
 a |   b   | c
---+-------+---
 a | one   | 1
 b | two   | 2
 c | three | 3
 d | four  | 4
 e | five  | 5
(5 rows)
=> \a
Output format is unaligned.
=> \t
Showing only tuples.
=> \pset fieldsep '\t'
Field separator is "    ".
=> \o dumpfile.txt
=> select * from my_table;
=> \o
=> \! cat dumpfile.txt
a       one     1
b       two     2
c       three   3
d       four    4
e       five    5

如果已登录到数据库节点之一,您可以通过向 vsql 传递正确的参数直接从命令行创建相同的输出文件:

$ vsql -U username -F $'\t' -At -o dumpfile.txt -c "SELECT * FROM my_table;"
Password:
$ cat dumpfile.txt
a       one     1
b       two     2
c       three   3
d       four    4
e       five    5

如果要将 null 值转换为如前文所述的唯一字符串,您可以添加实参 -P null='NULLNULLNULL'(或所选的任何唯一字符串)。

通过将 -w vsql 命令行选项添加到示例命令行,您可以在批处理脚本中使用命令以将数据导出自动化。但是,该脚本将包含纯文本格式的数据库密码。如果采用此方法,您应防止该批处理脚本受到未经授权的访问,还应让脚本使用具有受限访问权限的数据库用户帐户。

若要将字段分隔符值设置为一个控制字符,请使用 shell 的控制字符转义符号。在 Bash 中,可以使用美元符号 ($) 后跟加单引号的字符串的形式在实参中指定控制字符。此字符串可以包含 C 字符串转义(例如 \t 表示制表符)或反斜线 (\),后跟要使用的字符的八进制值。

以下示例演示了如何将分隔符字符设置为制表符 (\t)、垂直制表符 (\v) 以及垂直制表符的八进制值 (\013)。

$ vsql -At -c "SELECT * FROM testtable;"
A|1|2|3
B|4|5|6

$ vsql -F $'\t' -At -c "SELECT * FROM testtable;"
A       1       2       3
B       4       5       6

$ vsql -F $'\v' -At -c "SELECT * FROM testtable;"
A
 1
  2
   3
B
 4
  5
   6
$ vsql -F $'\013' -At -c "SELECT * FROM testtable;"
A
 1
  2
   3
B
 4
  5
   6

1.14 - 使用 vsql 复制数据

可以使用 vsql 在两个 Vertica 数据库之间复制数据。此技术类似于使用 vsql 导出数据中介绍的技术,但您应将一个 vsql 命令的输出传送到另一个 vsql 命令(从 STDIN 运行 COPY 语句)的输入,而不能让 vsql 将数据保存到文件以用于导出。此技术也适用于从输入流接受数据的其他数据库或应用程序。

使用 vsql 进行复制的最简单方法是登录到目标数据库的一个节点,然后发出用于连接到源 Vertica 数据库的 vsql 命令以转储所需数据。例如,以下命令可将节点 testdb01 上的 vmart 数据库中的 store.store_sales_fact 表复制到您已登录到的节点上的 vmart 数据库:

vsql -U username -w passwd -h testdb01 -d vmart -At -c "SELECT * from store.store_sales_fact"  \
| vsql -U username -w passwd -d vmart -c "COPY store.store_sales_fact FROM STDIN DELIMITER '|';"

如果使用的是 Bash shell,您可以对特殊分隔符进行转义。例如,DELIMITER E'\t' 可指定制表符。Bash 以外的其他 shell 可能具有其他字符串字面量语法。

监控进度(可选)

在 Vertica 数据库之间复制大量数据时,您可能希望使用某种方法来监控进度。监控复制操作的进度的方法之一是使用诸如 Pipe Viewer 等实用程序,此类实用程序直接将其输入传送到其输出并同时显示其传递的数据的数量和速度。如果提供所需处理的字节或行的总数,Pipe Viewer 甚至还能显示一个进度栏。可以通过运行将执行 SELECT COUNT 查询的单独的 vsql 命令来获取要处理的行数。

以下命令演示了如何使用 Pipe Viewer 监控前一个示例中所示的复制操作的进度。由于需要获取将复制的行数(可以在 Bash 反引号中使用单独的 vsql 命令来完成此操作,此 vsql 命令会执行字符串的内容并将命令的输出插入到命令行中),该命令变得复杂化。此 vsql 命令仅计算 in the store.store_sales_fact 表中的行数。

vsql -U username -w passwd -h testdb01 -d vmart -At -c "SELECT * from store.store_sales_fact"  \
| pv -lpetr -s `vsql -U username -w passwd -h testdb01 -d vmart -At -c "SELECT COUNT (*) FROM store.store_sales_fact;"` \
| vsql -U username -w passwd -d vmart -c "COPY store.store_sales_fact FROM STDIN DELIMITER '|';"

运行以上命令时,将显示一个如下所示的进度栏:

0:00:39 [12.6M/s] [=============================>                             ] 50% ETA 00:00:40

1.15 - 输出格式设置示例

默认情况下,Vertica 按如下方式设置查询输出的格式:

=> SELECT DISTINCT category_description FROM product_dimension ORDER BY category_description;
       category_description
----------------------------------
 Food
 Medical
 Misc
 Non-food
(4 rows)

您可以使用 \pset 命令以多种方式(例如更改边框)控制查询输出的格式:

=> \pset border 2
Border style is 2.
=> SELECT DISTINCT category_description FROM product_dimension ORDER BY category_description;
+----------------------------------+
|       category_description       |
+----------------------------------+
| Food                             |
| Medical                          |
| Misc                             |
| Non-food                         |
+----------------------------------+
(4 rows)
=> \pset border 0
Border style is 0.
=> SELECT DISTINCT category_description FROM product_dimension ORDER BY category_description;
      category_description
--------------------------------
Food
Medical
Misc
Non-food
(4 rows)

以下 pset 命令序列以多种方式更改查询输出:

  • 将边框样式设置为 1。

  • 移除列对齐。

  • 将字段分隔符更改为逗号。

  • 移除列标题

=> \pset border 1
Border style is 1.
=> \pset format unaligned
Output format is unaligned.
=> \pset fieldsep ','
Field separator is ",".
=> \pset tuples_only
Showing only tuples.
=> SELECT product_key, product_description, category_description FROM product_dimension LIMIT 10;
1,Brand #2 bagels,Food
1,Brand #1 butter,Food
2,Brand #6 chicken noodle soup,Food
3,Brand #11 vanilla ice cream,Food
4,Brand #14 chocolate chip cookies,Food
4,Brand #12 rash ointment,Medical
6,Brand #18 bananas,Food
7,Brand #25 basketball,Misc
8,Brand #27 french bread,Food
9,Brand #32 clams,Food

以下示例使用元命令来切换输出格式(在本例中为 \a(对齐)、\t(仅元组)和 -x(扩展显示)):

=> \a \t \x
Output format is aligned.
Tuples only is off.
Expanded display is off.
=> SELECT product_key, product_description, category_description FROM product_dimension LIMIT 10;
 product_key |       product_description        |       category_description
-------------+----------------------------------+----------------------------------
           1 | Brand #2 bagels                  | Food
           1 | Brand #1 butter                  | Food
           2 | Brand #6 chicken noodle soup     | Food
           3 | Brand #11 vanilla ice cream      | Food
           4 | Brand #14 chocolate chip cookies | Food
           4 | Brand #12 rash ointment          | Medical
           6 | Brand #18 bananas                | Food
           7 | Brand #25 basketball             | Misc
           8 | Brand #27 french bread           | Food
           9 | Brand #32 clams                  | Food
(10 rows)

以下示例将输出格式设置为 HTML,因此 Vertica 将 HTML 标记中的查询结果呈现为表:


=> \pset format html
Output format is html.
=> \pset tableattr 'border="2" cellpadding="3"'
Table attribute is "border="2" cellpadding="3"".
=> SELECT product_key, product_description, category_description FROM product_dimension LIMIT 2;
<table border="1" border="2" cellpadding="3">
  <tr>
    <th align="center">product_key</th>
    <th align="center">product_description</th>
    <th align="center">category_description</th>
  </tr>
  <tr valign="top">
    <td align="right">1</td>
    <td align="left">Brand #2 bagels</td>
    <td align="left">Food                            </td>
  </tr>
  <tr valign="top">
    <td align="right">1</td>
    <td align="left">Brand #1 butter</td>
    <td align="left">Food                            </td>
  </tr>
</table>
<p>(2 rows)<br />
</p>

2 - 客户端库

Vertica 客户端驱动程序库提供用于将客户端应用程序(或诸如 Cognos 和 MicroStrategy 等第三方应用程序)连接到 Vertica 数据库的接口。这些驱动程序简化了执行加载、报告生成及其他常见数据库任务时的数据交换操作。

以下是三种不同的客户端驱动程序:

  • 开放式数据库连接 (Open Database Connectivity, ODBC) — 最常用的接口,适用于采用 C、Python、PHP、Perl 和大多数其他语言编写的第三方应用程序和客户端。

  • Java 数据库连接 (Java Database Connectivity, JDBC) — 由采用 Java 编程语言编写的客户端使用。

  • 适用于 .NET 的 ActiveX 数据对象 (ActiveX Data Objects for .NET, ADO.NET) — 由采用 Microsoft .NET Framework 开发并采用 C#、Visual Basic .NET 和其他 .NET 语言编写的客户端使用。

客户端驱动程序标准

Vertica 客户端驱动程序与以下驱动程序标准兼容:

  • ODBC 驱动程序符合 3.5.1 版本的 ODBC 标准。

  • Vertica 的 JDBC 驱动程序是第 4 类驱动程序,符合 JDBC 3.0 标准。此驱动程序是使用 JDK 版本 1.5 编译的,并与使用 JDK 版本 1.5 和 1.6 编译的客户端应用程序兼容。

  • ADO.NET 驱动程序符合 .NET framework 3.0 规范。

驱动程序不支持这些标准中的部分可选功能。有关详细信息,请参阅 ODBC 功能支持JDBC 功能支持 以及使用 ADO.NET

2.1 - 客户端驱动程序和服务器版本兼容性

Vertica 服务器与客户端驱动程序之间的向后兼容性可双向工作;Vertica 服务器与所有先前版本的客户端驱动程序兼容,所有新的客户端驱动程序都与大多数版本的 Vertica 服务器兼容。这种兼容性使您无需立即升级客户端软件即可升级 Vertica 服务器,并且可以将新客户端软件与旧版本的 Vertica 一起使用。但有时,新服务器版本中的个别功能可能在旧版驱动程序中不可用。

2.2 - 客户端驱动程序

必须安装 Vertica 客户端驱动程序才能从客户端应用程序访问 Vertica。驱动程序将创建并维护与数据库的连接,而且为应用程序提供用于访问数据的 API。客户端驱动程序支持使用 JDBC、ODBC 和 ADO.NET 的连接。

客户端驱动程序标准

客户端驱动程序支持以下标准:

  • ODBC 驱动程序符合 ODBC 3.5.1 规范。

  • JDBC 驱动程序符合 JDK 5 规范。

  • ADO.NET 驱动程序符合 .NET framework 3.0 规范。

2.2.1 - 安装和配置客户端驱动程序

通过安装适当的客户端驱动程序,您可以使用各种编程语言和工具访问 Vertica 数据库。下表列出了每种访问方法所需的客户端驱动程序:

2.2.1.1 - Windows 客户端驱动程序安装程序

Vertica 客户端驱动程序和工具安装程序中包含所有可用的 Windows 客户端驱动程序。这会在满足先决条件的系统上安装以下组件。个别组件在使用前可能需要额外配置,因此请导航至下方链接的页面以获取更多信息:

2.2.1.1.1 - 系统先决条件

适用于 Windows 的 Vertica 客户端驱动程序和工具要求系统满足基本的先决条件。该包还要求安装特定的 Microsoft 组件以实现完全集成。

有关所有先决条件的列表,请参阅支持的平台文档中的客户端驱动程序支持

完全更新系统

在安装 Vertica 驱动程序包之前,请确认系统已使用所有 Windows 更新和修补程序进行了完全更新。有关如何运行 Windows 更新的说明,请参阅您的 Windows 版本的文档。Vertica 客户端库和 vsql 可执行文件安装依赖于 Windows Service Pack 的更新的 Windows 库。确保解决阻止安装 Windows 更新的任何问题。

如果您的系统不是完全最新,您可能会在启动 vsql 时收到有关缺少库(例如 api-ms-win-crt-runtime-l1-1-0.dll)的错误消息。

2.2.1.1.1.1 - .NET Framework

.NET Framework 未捆绑到适用于 Windows 的 Vertica 客户端驱动程序和工具中。但是,在安装期间,如果在系统上检测不到 Microsoft .NET 3.5 SP1,则 Web 安装程序会启动。然后,您可以下载该框架。此外,如果操作系统版本包含 .NET 3.5 SP1,但未打开此功能,则安装程序会打开此功能。

如果已安装 Visual Studio 2010 或 2012,则您的系统已包含 Microsoft .NET Framework 4.0 或 4.5。您还需要安装 Microsoft .NET 3.5 SP1,才能使用 Vertica 客户端驱动程序和工具来实现 Windows 集成功能。

可以使用以下链接直接从 Microsoft 下载相应的 .NET Framework 版本:

2.2.1.1.1.2 - Microsoft visual studio

适用于 Windows 安装程序的 Vertica 客户端驱动程序和工具提供一个 Visual Studio 插件,通过该插件,您可以使用 Vertica 作为 Visual Studio 2008、Visual Studio 2010、Visual Studio 2012、Visual Studio 2013 或 Visual Studio 2015 的 Visual Studio 数据源。该插件的连接属性与 ADO.NET 连接属性 相同。

安装该插件之后,您可以使用该插件从 Visual Studio 中访问 Vertica 数据库。如果尚未安装 SDK,请下载特定于您的 Visual Studio 版本的 SDK。

如果开始安装时缺少 Microsoft Visual Studio SDK,将打开一个对话框进行安装。您可以选择忽略此对话框。

配置 BIDS 集成或 SSDT-BI 集成

适用于 Windows 安装程序的 Vertica 客户端驱动程序和工具提供 BIDS (Visual Studio 2008) 集成或 SSDT-BI(Visual Studio 2010、Visual Studio 2012、Visual Studio 2013 或 Visual Studio 2015)集成。若要使用 BIDS 或 SSDT-BI,请遵循以下过程:

  1. 安装适用于 Visual Studio 的 BIDS 或 SSDT-BI 开发工具加载项。

  2. 验证 SQL Server 是安装在同一台还是不同计算机上。

  3. 验证是否已激活适用于 IDS 或 SSDT-BI 的 SQL Server 共享功能。

然后,您可以使用 BIDS 或 SSDT-BI 开发程序包,还可以使用 SQL Server 的 SSIS、SSAS 和 SSRS 功能创建项目。若要使用这些功能,您必须通过 Vertica ADO.NET 驱动程序(适用于 SSIS 和 SSRS)或 OLE DB 驱动程序(适用于 SSAS)连接到 Vertica。

有关详细信息,请参阅Microsoft 组件

2.2.1.1.1.3 - Microsoft SQL server

使用 SQL Server 2012、SQL Server 2014 或 SQL Server 2016。适用于 Windows 安装程序的 Vertica 客户端驱动程序和工具支持以下功能:

  • SQL Server 2012、SQL Server 2014 和 SQL Server 2016:

    • SQL Server Integration Services (SSIS)

    • SQL Server Reporting Services (SSRS)

    • SQL Server Analysis Services (SSAS)

  • 使用 Visual Studio 2010、Visual Studio 2012、Visual Studio 2013 和 Visual Studio 2015 的 SQL Server — SQL Server Data Tools - Business Intelligence (SSDT-BI)

若要使用增强的 Vertica .NET 支持,您必须先安装 SQL Server。然后,您可以安装适用于 Windows 的客户端驱动程序和工具。必须在 SQL Server 上安装以下组件:

2.2.1.1.2 - 适用于 Windows 的 Visual Studio 插件

适用于 Windows 的 Visual Studio 插件作为适用于 Windows 的客户端驱动程序和工具的一部分安装。

有关 Visual Studio 插件如何与先前已安装在系统上的 Microsoft 组件集成的信息,请参阅 Microsoft 组件

2.2.1.1.2.1 - Visual Studio 限制

Visual Studio 2012 可能需要 Update 3

在下列情况下,您可能需要安装用于 Visual Studio 2012 的 Update 3:

  • 启动 Server Explorer 以查看和使用 Vertica 服务器,但 Vertica 数据源不可见。

  • 创建 SSAS 多维数据集,连接到 Vertica,并找到空的表列表或表无法正常工作。

对于其他受 Vertica 支持版本的 Visual Studio,此问题不会出现。

结果查看器限制为 655 列

Visual Studio 结果查看器无法执行包含超过 655 列的查询。如果表包含超过 655 列,请选择特定列(总数最多为 655),而不应选择所有列。

手动刷新 Visual Studio 的设置

安装 Visual Studio 插件后,如果 Vertica 未作为数据提供程序列出,请手动刷新。

若要执行此操作,请运行 devenv.exe/setup,您可以在 Visual Studio 的安装文件夹中找到该文件。

SQL 窗格问题

  • ALTER TABLECREATE TABLE

    您使用 Visual Studio 2008、Visual Studio 2010、Visual Studio 2012、Visual Studio 2013 或 Visual Studio 2015,并在 SQL 窗格中发出 ALTER TABLE 或 CREATE TABLE 语句。但显示一条消息,指明该语句不受支持。若要解决该错误,请单击继续,查询将会执行。

  • 带有分号的查询

    您使用 Visual Studio 2008、Visual Studio 2010、Visual Studio 2012、Visual Studio 2013 或 Visual Studio 2015,并在 SQL 窗格中执行 SQL 查询。如果在查询中包含分号 (;),该查询将会执行,但您无法编辑返回的结果。若要避免此问题,请在 SQL 窗格中输入不带分号的相同查询。

  • 引用布尔值

    您使用 Visual Studio 2008、Visual Studio 2010、Visual Studio 2012、Visual Studio 2013 或 Visual Studio 2015 连接到 Vertica 数据库,并在 SQL 窗格中执行 SQL 查询。当尝试将值插入布尔列而不在值两端加上引号时,SQL 语句的后续执行将返回错误。要解决此问题,请加上引号。

卸载 适用于 Windows 的客户端驱动程序和工具错误

在某些情况下,Windows 软件包的客户端驱动程序和工具的卸载失败并显示需要 .NET Framework的消息。以下是导致此问题的场景。

  1. 安装 Windows 的客户端驱动程序和工具
  2. 然后安装 Visual Studio 2010 或 2012,其中包括安装 .NET Framework 4.0 或 4.5。
  3. 使用 Windows 控制面板卸载 .NET Framework。
  4. 然后,您尝试卸载 Windows 的客户端驱动程序和工具。卸载将会失败并显示需要 .NET Framework的消息。

要更正此问题,请执行以下操作:

  1. 使用 Windows 控制面板手动重新安装 .NET Framework 4.0 或 4.5。
  2. 卸载 Windows 的客户端驱动程序和工具

2.2.1.1.3 - 卸载、修改或修复客户端驱动程序和工具

要卸载、修改或修复客户端驱动程序和工具,请运行适用于 Windows 的客户端驱动程序和工具安装程序。

该安装程序提供以下三个选项:

在静默模式下卸载客户端驱动程序和工具

  1. 以 Windows 管理员身份,打开命令行会话,然后将目录更改为包含安装程序的文件夹。

  2. 运行命令:

    VerticaSetup.exe -q -uninstall
    

将在静默模式下卸载客户端驱动程序和工具。

2.2.1.2 - FIPS 客户端驱动程序

Vertica 提供符合 FIPS 的版本的 ODBC 和 JDBC 客户端驱动程序。

2.2.1.2.1 - 为 JDBC 安装 FIPS 客户端驱动程序

Vertica 提供符合联邦信息处理标准 (FIPS) 的 JDBC 客户端驱动程序。使用此 JDBC 客户端驱动程序访问与 FIPS 兼容的系统。有关 FIPS 的详细信息,请参阅美国联邦信息处理标准

在 JDBC 客户端上实施 FIPS 需要一个名为 BouncyCastle 的第三方 JRE 扩展,它是一组用于加密的 API。将 BouncyCastle API 与 JDK 1.7 和 1.8 以及支持的符合 FIPS 的操作系统一起使用。

以下过程将 FIPS BouncyCastle .jar 添加为 JVM JSSE 提供程序:

  1. 下载 BouncyCastle FIPS .jar 文件 bc-fips-1.0.0.jar

  2. bc-fips-1.0.0.jar 添加为 JRE 库扩展:

    path/to/jre/lib/ext/bc-fips-1.0.0.jar
    
  3. <path to jre>/lib/security/java.security 中将 BouncyCastle 添加为 SSL 安全提供程序:

    security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastle FipsProvider
    security.provider.2=com.sun.net.ssl.internal.ssl.Provider BCFIPS
    security.provider.3=sun.security.provider.Sun
    
  4. 使用以下 JVM java -D 系统属性命令实参将 KeyStore 和 TrustStore 文件设置为 BCFIPS:

    export JAVA_OPTS="$JAVA_OPTS -Djavax.net.ssl.keyStoreProvider=BCFIPS
    export JAVA_OPTS="$JAVA_OPTS -Djavax.net.ssl.trustStoreProvider=BCFIPS
    
  5. path/to/jre/lib/security/java.security 中将 KeyStore 实施的默认类型设置为 BCFKS:

    keystore type=BCFKS
    ssl.keystore.type=BCFKS
    
  6. 创建 BCFKS 类型的密钥库和信任库:

    cd path/to/jre
    -storetype BCFKS
    -providername BCFIPS
    -providerclass org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
    -provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
    -providerpath bc-fips-1.0.0.jar
    -alias CARoot
    -import -file path/to/server.crt.der
    
  7. 出现提示时,输入密钥库密码。系统将显示以下消息以确认已将证书添加到密钥库:

    "Certificate was added to the keystore"
    
  8. 使用 SSL DB 运行 Java 程序:

    1. 将 vertica.kafka.keystore.bcfks 密钥库从 path/to/jre/lib/ext/ 复制到 Java 程序文件夹。

    2. 将 Vertica 服务器证书转换为能够由 Java 理解的形式:

      $ path/to/java/bin/keytool -keystore verticastore -keypasswd -storepass password
                              -importkeystore -noprompt -alias verticasql -import -file server.crt.der
      
    3. 安装 JDBC

  9. 对实施进行测试:

    $ java -Djavax.net.debug=ssl -Djavax.net.ssl.keyStore='vertica.kafka.keystore.bcfks'
    -Djavax.net.ssl.keyStorePassword='password'
    -Djavax.net.ssl.trustStore='path/to/verticastore'
    -Djavax.net.ssl.trustStorePassword='password'
    -cp .:vertica-jdbc-12.0.0-0.jar FIPSTest
    

2.2.1.2.2 - 为 ODBC 和 vsql 安装 FIPS 客户端驱动程序

Vertica 为与 FIPS 兼容的系统提供 FIPS 客户端。与 FIPS 兼容的系统启用了 FIPS,其中包含 OpenSSL 库。

FIPS 客户端支持 ODBC 和 vsql,仅提供 64 位版本。

先决条件

确认主机系统正在运行受 Vertica 支持且符合 FIPS 的操作系统

FIPS 客户端安装程序会检查您的主机系统以获取 sysctl 参数 crypto.fips_enabled 的值。您必须将此参数设置为 1(启用)。如果未启用您的主机,则客户端不会安装。

安装 FIPS 客户端

要安装 FIPS 客户端驱动程序包:

  1. Vertica 驱动程序下载页面 下载 FIPS 客户端包。

  2. 以 root 身份登录客户端系统。

  3. 安装已下载的 RPM 包:

    # rpm -Uvh package_name.rpm
    

对于 ODBC,在您安装了客户端包之后,创建 DSN 并设置一些额外的配置参数。有关详细信息,请参阅:

您可以选择将 vsql 客户端添加到 PATH 环境变量,以便不需要输入完整路径即可运行该客户端。为此,将下行添加到主目录中的 .profile 文件或全局 /etc/profile 文件:

export PATH=$PATH:/opt/vertica/bin

客户端如何搜索 OpenSSL 库

当您启动客户端应用程序以连接到服务器时,客户端会为支持的 OpenSSL 版本搜索并加载 OpenSSL 库 libcrypto.so.10libssl.so.10

  • 客户端首先检查是否已设置 LD_LIBRARY_PATH

  • 如果 LD_LIBARY_PATH 位置不包含库,它会检查 RunPath/opt/vertica/lib 或 ODBC 或 vsql 目录结构 (../lib)。

下图描述了 OpenSSL 库的搜索过程:

2.2.1.3 - JDBC 客户端驱动程序

Vertica JDBC 客户端驱动程序符合 JDK 5 规范,它提供了一个接口,通过该接口可以使用 Java 与 Vertica 数据库进行通信。有关此 API 和其他 API 的详细信息,请参阅 API 参考

要安装 JDBC 客户端驱动程序,请参阅安装 JDBC 客户端驱动程序

2.2.1.3.1 - 安装 JDBC 客户端驱动程序

JDBC 客户端驱动程序符合 JDK 5 规范。根据您的环境和要求下载 JDBC 客户端驱动程序。如果您需要使用符合 FIPS 的驱动程序,请参阅为 JDBC 安装 FIPS 客户端驱动程序

从 RPM 安装 Vertica 会自动安装 JDBC 客户端驱动程序。要使用 JDBC 客户端驱动程序,您只需将 Vertica JDBC .jar 添加到您的 CLASSPATH

要手动安装 JDBC 客户端驱动程序:

  1. 从客户端驱动程序下载页面,下载与您的 Vertica 版本兼容的 JDBC 客户端驱动程序版本。

  2. 在希望从中访问 Vertica 的每个客户端系统上将 .jar 文件复制到 Java CLASSPATH 中的目录。您可以执行以下操作之一:

    • .jar 文件复制到其自己的目录(例如 /opt/vertica/java/lib),然后将该目录添加到 CLASSPATH(建议采用此方法)。有关详细信息,请参阅修改 Java CLASSPATH

    • .jar 文件复制到已存在于 CLASSPATH 中的目录(例如,已将应用程序所依赖的其他 .jar 文件放置到的目录)。

    • .jar 文件复制到系统范围的 Java 扩展目录。确切的位置因操作系统不同而异。一些示例包括:

      • Windows: C:\Program Files\Java\jrex.x.x_x\lib\ext\\

      • Mac OS: /Library/Java/Extensions/Users/username/Library/Java/Extensions

  3. 创建连接以测试您的配置。

2.2.1.3.2 - 修改 Java CLASSPATH

CLASSPATH 环境变量包含目录的列表,Java 运行时将从该列表的目录查找库类文件。要让 Java 客户端代码访问 Vertica,您必须将包含 Vertica JDBC .jar 的目录添加到 CLASSPATH。

将符号链接用于 CLASSPATH

您可以选择将符号链接 vertica-jdbc-x.x.x.jar(其中 x.x.x 是版本号,此符号链接指向 JDBC 库 .jar 文件而非 .jar 文件本身)添加到 CLASSPATH。

使用符号链接可确保对 JDBC 库 .jar 文件(此文件使用其他文件名)进行的任何更新不会使 CLASSPATH 设置失效,因为符号链接的文件名将保持不变。您只需要更新符号链接,使其指向新的 .jar 文件。

Linux 和 OS X

以下示例使用符合 POSIX 的 shell。

要为当前会话设置 CLASSPATH:

$ export CLASSPATH=$CLASSPATH:/opt/vertica/java/lib/vertica-jdbc-x.x.x.jar

要为每个会话设置 CLASSPATH,请将以下内容添加到启动文件(例如 ~/.profile/etc/profile):

$ export CLASSPATH=$CLASSPATH:/opt/vertica/java/lib/vertica-jdbc-x.x.x.jar

Windows

提供指向 .jar.zip.class 文件的类路径。

C:> SET CLASSPATH=classpath1;classpath2...

例如:

C:> SET CLASSPATH=C:\java\MyClasses\vertica-jdbc-x.x.x.jar

与 Linux/UNIX 设置一样,此设置仅持续到当前会话关闭。若要永久设置 CLASSPATH,请设置环境变量:

  1. 在 Windows 控制面板中,单击系统

  2. 单击高级高级系统设置

  3. 单击环境变量

  4. 在“用户变量”下,单击新建

  5. 在“变量名”框中,键入 CLASSPATH

  6. 在“变量值 (Variable value)”框中,键入指向系统上的 Vertica JDBC .jar 文件的路径(例如, C:\Program Files (x86)\Vertica\JDBC\vertica-jdbc-x.x.x.jar

在 Java 命令中指定库目录

另一种与操作系统无关的、告知 Java 运行时在何处可以找到 Vertica JDBC 驱动程序的方法是:使用 -cp-classpath 实参将包含 .jar 文件的目录显式添加到 Java 命令行。例如,可以使用以下命令启动客户端应用程序:

java -classpath /opt/vertica/java/lib/vertica-jdbc-x.x.x.jar myapplication.class

Java IDE 还可让您将目录添加到 CLASSPATH,或者可让您将 Vertica JDBC 驱动程序导入到项目中。有关详细信息,请参阅 IDE 文档。

2.2.1.4 - ODBC 客户端驱动程序

Vertica ODBC 客户端驱动程序提供了一个接口,通过该接口可以使用多种语言创建客户端应用程序:

要安装 ODBC,请参阅安装 ODBC 客户端驱动程序

2.2.1.4.1 - 安装 ODBC 客户端驱动程序

要安装 ODBC,请按照您的平台说明进行操作。有关受支持平台的列表,请参阅客户端驱动程序支持

本页介绍非 FIPS 安装。要在符合 FIPS 的系统上安装 ODBC,请参阅为 ODBC 和 vsql 安装 FIPS 客户端驱动程序

在 Linux 上安装

从 RPM 安装 Vertica 会自动安装 ODBC 客户端驱动程序,因此您无需在运行 Vertica 的计算机上再次安装它们。要在这种情况下使用 ODBC 客户端驱动程序,请创建 DSN

要在其他计算机上手动安装 ODBC 客户端驱动程序:

  1. 以 root 身份登录客户端系统。

  2. 验证您的系统是否装有受支持的 ODBC 驱动程序管理器。

  3. 以适合您的发行版的格式,下载适用于 Linux 的 ODBC 客户端驱动程序

  4. 安装或解压缩驱动程序:

    • 如果您下载了 .rpm,请安装该驱动程序:

      $ rpm -Uvh driver_name.rpm
      
    • 如果您下载了 .tar,则创建 /opt/vertica/ 目录(如果该目录不存在),将 .tar 复制到其中,导航到它,然后解压缩 .tar

      
      $ mkdir -p /opt/vertica/
      $ cp driver_name.tar.gz /opt/vertica/
      $ tar vzxf driver_name.tar.gz
      

      这将创建两个目录:

      • /opt/vertica/include:包含头文件。

      • /opt/vertica/lib64/(64 位)或 /opt/vertica/lib/(32 位):包含库文件。

  5. vertica.ini 中设置以下 ODBC 驱动程序设置。有关每个设置的详细信息,请参阅 ODBC 驱动程序设置

    • ErrorMessagesPath:必需,包含 ODBC 驱动程序的错误消息文件的目录路径。

    • ODBCInstLib:ODBC 安装程序库的路径。仅当驱动管理器的安装库不在环境变量 LD_LIBRARY_PATHLIB_PATH 中时才需要。

    • DriverManagerEncoding:驱动程序管理器使用的 UTF 编码标准。仅当您的驱动程序管理器不使用 UTF-8 时才需要。

    下面是 vertica.ini 中的示例配置:

    • 使用 64 位 UNIXODBC 驱动程序管理器的编码。

    • 使用标准 Vertica 64 位 ODBC 驱动程序安装目录中定义的错误消息。

    • 将所有警告和严重性更高的消息记录到日志文件 /tmp/

      [Driver]
      DriverManagerEncoding=UTF-16
      ODBCInstLib=/usr/lib64/libodbcinst.so
      ErrorMessagesPath=/opt/vertica
      LogLevel=4
      LogPath=/tmp
      
  6. 创建 DSN

在 macOS 上安装

要在 macOS 上安装 ODBC 客户端驱动程序:

  1. 验证您的系统是否装有兼容的驱动程序管理器。该驱动程序旨在与 macOS 附带的标准 iODBC 驱动程序管理器一起使用。您也可以使用 unixODBC

  2. 下载 ODBC 客户端驱动程序。

  3. 如果已安装以前版本的 ODBC 驱动程序,则系统可能已注册了名为“Vertica”的驱动程序。在从 .pkg 安装程序安装新版本之前,您必须移除或重命名旧版本的驱动程序。重命名旧版本可让您在安装新版本后保留旧版本。

  4. 运行安装程序。

  5. 创建 DSN

静默安装

  1. 通过以下两种方式之一登录客户端 macOS:

    • 以管理员帐户登录(如果您安装驱动程序是为了在系统范围使用)。

    • 以需要使用 Vertica ODBC 驱动程序的用户身份登录。

  2. 打开终端。

  3. 使用以下命令安装包含 ODBC 驱动程序的 .pkg 文件:

    sudo installer -pkg path/to/client/driver/vertica-odbc-xx.x.x-x.pkg -target /
    

在 Windows 上安装

要在 Windows 上安装 ODBC 客户端驱动程序:

  1. 下载适用于 Windows 的客户端驱动程序安装程序。

  2. 运行安装程序。

  3. 创建 DSN

静默安装

  1. 以管理员身份打开终端。

  2. 运行以下命令将驱动程序静默安装到 C:\Program Files\Vertica Systems

    VerticaSetup.exe -q -install InstallFolder="C:\Program Files\Vertica Systems"
    

2.2.1.4.2 - 升级和降级 ODBC

Linux

要升级 ODBC:

  1. 卸载当前版本的驱动程序。

  2. 安装新版本的驱动程序。

macOS

要升级或降级 ODBC:

  • 升级:新安装的适用于 macOS 的 Vertica ODBC 驱动程序版本会自动升级相关的驱动程序系统设置。除了开始使用较新版本的驱动程序之外,与以前版本的驱动程序关联的任何 DSN 不受影响。

  • 降级:运行卸载脚本以移除适用于 macOS 的 Vertica ODBC 驱动程序的当前版本。在安装旧版本的驱动程序之前完成此步骤。

Windows

  1. 下载 Windows 客户端驱动安装程序。

  2. 运行安装程序并按照提示升级驱动程序。安装程序会升级现有的驱动程序。

  3. 重新启动系统。

2.2.1.4.3 - 卸载 ODBC

Linux

如果您使用 .rpm 安装了 ODBC:

$ rpm -e package_name

如果您使用 .tar 安装了 ODBC,请手动删除该目录。

macOS

卸载 macOS ODBC 客户端驱动程序不会移除与该驱动程序关联的任何现有 DSN。

若要卸载,请执行下列操作:

  1. 打开终端窗口。

  2. 运行命令:

    sudo /Library/Vertica/ODBC/bin/Uninstall
    

Windows

  1. 打开添加或删除程序 (Add or Remove Programs) 菜单。

  2. 或者卸载 Vertica Client Installer 以从系统中移除所有客户端驱动程序,或者仅卸载 ODBC 以卸载以下应用程序:

    • Vertica ODBC 驱动程序(32 位)

    • Vertica ODBC 驱动程序(64 位)

2.2.1.4.4 - 创建 ODBC 数据源名称 (DSN)

数据源名称 (DSN) 是由开放式数据库连接 (ODBC) 使用的逻辑名称,用于引用从数据源访问数据而所需的驱动程序和其他信息。无论您是开发自己的 ODBC 客户端代码,还是使用需要通过 ODBC 访问 Vertica 的第三方工具,都需要配置和测试 DSN。您使用的方法取决于所用的客户端操作系统。

有关特定于您的客户端操作系统的信息,请参阅以下部分。

2.2.1.4.4.1 - 为 Linux 创建 ODBC DSN

在 Linux 和其他类 UNIX 平台上,您可以在文本文件中定义 DSN。客户端的驱动程序管理器会读取该文件,以确定如何连接到 Vertica 数据库。驱动程序管理器通常在以下两个位置查找 DSN 定义:

  • /etc/odbc.ini

  • ~/.odbc.ini (用户主目录中名为 .odbc.ini 的文件)

用户必须能够读取 odbc.ini 文件,才能使用它连接到数据库。如果使用全局 odbc.ini 文件,请考虑创建一个对该文件具有读取权限的 UNIX 组。然后,将需要使用 DSN 的用户添加到该组。

这些文件的结构是相同的,只是所在位置不同而已。如果这两个文件都存在,则 ~/.odbc.ini 文件通常会替代系统范围 /etc/odbc.ini 文件。

odbc.ini 文件结构

odbc.ini 是一个包含以下两种类型的行的文本文件:

  • 节定义,是用方括号括起来的文本字符串。

  • 参数定义,其中依次包含参数名称、等号 (=) 和参数值。

文件的第一节始终名为 [ODBC Data Sources],其中包含 odbc.ini 文件所定义的所有 DSN 的列表。此节中的参数是 DSN 的名称,这些参数稍后在文件中显示为节定义。值是 DSN 的文本描述,并不起任何作用。例如,定义了名为 Vertica DSN 的单个 DSN 的 odbc.ini 文件可能包含以下 ODBC 数据源节:

[ODBC Data Sources]
VerticaDSN = "vmartdb"

显示在 ODBC 数据源节之后的节定义了每个 DSN。DSN 节的名称必须与 ODBC 数据源节中定义的名称之一匹配。

配置 odbc.ini 文件:

若要创建或编辑 DSN 定义文件,请执行下列操作:

  1. 使用所选文本编辑器打开 odbc.ini~/.odbc.ini

  2. 创建 ODBC 数据源节并定义参数:

    • 其名称是要创建的 DSN 的名称

    • 其值是 DSN 的描述

    例如,若要创建名为 VMart 的 DSN,您应输入以下命令:

    [ODBC Data Sources]
    VMart = "VMart database on Vertica"
    
  3. 创建名称与您在步骤 2 中定义的 DSN 名称匹配的节。在此节中,您应添加定义了 DSN 的设置的参数。最常定义的参数如下:

    • Description – 有关数据源的附加信息。

    • Driver – Vertica ODBC 驱动程序的位置和指定,或 odbcinst.ini 文件中定义的驱动程序的名称(请参阅下文)。为了确保将来的兼容性,请使用库目录(而非库文件)中的符号链接的名称:

      • /opt/vertica/lib(在 32 位客户端上)

      • /opt/vertica/lib64,(在 64 位客户端上)

      例如,64 位 ODBC 驱动程序库的符号链接是:

      /opt/vertica/lib64/libverticaodbc.so
      

      符号链接始终指向最新版本的 Vertica 客户端 ODBC 库。请使用此链接,以便不需要在更新客户端驱动程序后更新所有 DSN。

    • Database – 在服务器中运行的数据库的名称。此示例使用 vmartdb 来表示 vmartdb。

    • ServerName — 安装了 Vertica 的服务器的名称。如果 Vertica 已安装在同一台计算机上,则使用 localhost。

      您可以提供 IPv4 地址、IPv6 地址或主机名。

      在 IPv4/IPv6 混合网络中,DNS 服务器配置决定了哪个 IP 版本地址最先发送。可使用 PreferredAddressFamily 选项来强制连接使用 IPv4 或 IPv6。

    • UID — 数据库超级用户(名称与数据库管理员帐户相同)或超级用户已创建并向其授予了权限的用户。此示例使用用户名 dbadmin。

    • PWD — 指定的用户名的密码。此示例将密码字段留空。

    • Port — Vertica 用来侦听 ODBC 连接的端口号。例如,5433。

    • ConnSettings — 可以包含用分号分隔的 SQL 命令。这些命令可以在连接到服务器之后立即运行。

    • SSLKeyFile — 客户端私钥的文件路径和名称。此文件可以驻留在系统上的任意位置。

    • SSLCertFile — 客户端公用证书的文件路径和名称。此文件可以驻留在系统上的任意位置。

    • Locale — 用于会话的默认区域设置。默认情况下,数据库的区域设置是:en_US@collation=binary (English as in the United States of America)。将区域设置指定为 ICU 区域设置。有关可用于指定区域设置的完整参数列表,请参阅 ICU 用户指南 (http://userguide.icu-project.org/locale)。

    • PreferredAddressFamily

      如果客户端和服务器都有 IPv4 和 IPv6 地址而且您已经提供了主机名时要使用的 IP 版本,则使用的 IP 版本是以下之一:

      • ipv4:使用 IPv4 连接到服务器。

      • ipv6:使用 IPv6 连接到服务器。

      • none:使用 DNS 服务器提供的 IP 地址。

例如:

[VMart]
Description = Vmart Database
Driver = /opt/vertica/lib64/libverticaodbc.so
Database = vmartdb
Servername = host01
UID = dbadmin
PWD =
Port = 5433
ConnSettings =
AutoCommit = 0
SSLKeyFile = /home/dbadmin/client.key
SSLCertFile = /home/dbadmin/client.crt
Locale = en_US@collation=binary

有关包括 Vertica 特定参数的完整参数列表,请参阅 ODBC DSN 连接属性

使用 odbcinst.ini 文件

您可以使用在 odbcinst.ini 文件中定义的驱动程序的名称,而不必在 DSN 定义中指定 ODBC 驱动程序库的路径。如果您有许多 DSN 并且经常需要更新这些 DSN 以指向新的驱动程序库,则此方法很有用。使用此方法,您还可以设置一些其他 ODBC 参数,例如线程模型。

odbc.ini 文件一样,odbcinst.ini 文件中也包含一些节。每个节定义了可在 odbc.ini 文件中引用的 ODBC 驱动程序。

在一个节中,您可以定义以下参数:

  • Description — 有关数据源的附加信息。

  • Driver — Vertica ODBC 驱动程序的位置和指定,例如 /opt/vertica/lib64/libverticaodbc.so

例如:

[Vertica]
Description = Vertica ODBC Driver
Driver = /opt/vertica/lib64/libverticaodbc.so

然后,在 odbc.ini 文件中,您可以使用已在 odbcinst.ini 文件中创建的节(描述了要使用的驱动程序)的名称。例如:

[VMart]
Description = Vertica Vmart database
Driver = Vertica

如果使用的是 unixODBC 驱动程序管理器,您还应添加一个 ODBC 节以替代其标准线程设置。默认情况下,unixODBC 通过 ODBC 将所有 SQL 调用序列化,这样可以阻止多个并行加载。若要更改其默认行为,请将以下行添加到 odbcinst.ini 文件中:

[ODBC]
Threading = 1

配置其他 ODBC 设置

在 Linux 和 UNIX 系统上,您需要先配置一些其他驱动程序设置,然后才能使用 DSN。有关详细信息,请参阅ODBC 驱动程序设置

2.2.1.4.4.1.1 - 使用 isql 测试 ODBC DSN

unixODBC 驱动程序管理器包含一个名为 isql 的实用程序,此实用程序是一个简单的 ODBC 命令行客户端。使用此实用程序,您可以连接到 DSN 以发送命令和接收结果,类似于 vsql。

若要使用 isql 测试 DSN 连接,请执行下列操作:

  1. 运行以下命令:

    $ isql –v DSNname
    

    其中 DSNname 是已创建的 DSN 的名称。

    此时将显示连接消息和 SQL 提示。如果它们未显示,则可能表明存在配置问题,或者可能表明所使用的用户名或密码不正确。

  2. 尝试执行简单 SQL 语句。例如:

    SQL> SELECT table_name FROM tables;
    

    isql 工具将返回 SQL 语句的结果。

2.2.1.4.4.2 - 为 Windows 客户端创建 ODBC DSN

若要为 Microsoft Windows 客户端创建 DSN,您必须执行以下任务:

2.2.1.4.4.2.1 - 设置 ODBC DSN

数据源名称 (DSN) 是 ODBC 逻辑名称,用来表示驱动程序和访问数据源中的数据所需的其他信息。此名称由 Internet 信息服务 (Internet Information Services, IIS) 用于与 ODBC 数据源的连接。

此部分介绍如何使用 Vertica ODBC 驱动程序设置 ODBC DSN。本主题假设您已按照在 Windows 中安装客户端驱动程序中所述安装了驱动程序。

设置 DSN

  1. 打开 ODBC 管理器。例如,可以导航到“开始 > 控制面板 > 管理工具 (Administration Tools) > 数据源 (ODBC)”。

  2. 确定是否希望客户端系统上的所有用户都能访问 Vertica 数据库的 DSN。

    • 如果想让所有用户都可访问,则单击系统 DSN (System DSN) 选项卡。

    • 否则,单击用户 DSN 选项卡以创建仅供您的 Windows 用户帐户使用的 DSN。

  3. 单击添加 (Add) 以创建用于连接到 Vertica 数据库的新的 DSN。

  4. 滚动浏览“创建新数据源 (Create a New Data Source)”对话框中的驱动程序列表以查找 Vertica 驱动程序。选择该驱动程序,然后单击完成

    此时将显示 Vertica ODBC DSN 配置对话框。

  5. 单击更多 >>> (More >>>) 按钮,以查看正在编辑的字段和由 DSN 定义的连接字符串的描述。

  6. 输入 DSN 的信息。以下字段是必填的:

    • DSN 名称 — DSN 的名称。客户端使用此名称来标识要连接到的 DSN。DSN 名称必须满足以下要求:

      • 最大长度为 32 个字符。

      • 它由 ASCII 字符组成,但以下字符除外: { } , ; ? * = ! @ \

      • 它不包含空格。

    • 服务器 — 要连接到的 Vertica 服务器的主机名或 IP 地址。如果 Vertica 已安装在同一台计算机上,则使用 localhost。

      您可以提供 IPv4 地址、IPv6 地址或主机名。

      在 IPv4/IPv6 混合网络中,DNS 服务器配置决定了哪个 IP 版本地址最先发送。可使用 PreferredAddressFamily 选项来强制连接使用 IPv4 或 IPv6。

      PreferredAddressFamily 选项在“客户端设置”选项卡上可用。

    • 备份服务器 — 用于在“服务器”字段中指定的服务器已关闭时连接到的主机名或 IP 地址的逗号分隔列表。可选。

    • 数据库 — Vertica 数据库的名称。

    • 用户名 — 在连接到数据库时使用的用户帐户的名称。在连接到 DSN 时,如果应用程序不提供自己的用户名,则会使用此帐户名称登录到数据库。

    其余字段是可选的。有关可以定义的 DSN 参数的详细信息,请参阅 DSN 参数

  7. 如果要测试连接,请执行下列操作:

    1. 至少输入有效的 DSN 名称服务器名称数据库,并输入用户名 或选择 Windows 身份验证

    2. 如果未选择 Windows 身份验证,您可以在密码 框中输入密码。或者,您也可以选择密码提示,以让驱动程序在连接时提示您输入密码。

    3. 单击测试连接

  8. 完成编辑和测试 DSN 后,单击确定。Vertica ODBC DSN 配置窗口将关闭,并且新的 DSN 会在“ODBC 数据源管理器”窗口中列出。

  9. 单击确定 (OK) 以关闭 ODBC 数据源管理器。

创建 DSN 后,您可以使用 Microsoft Excel 2007 进行测试。

在 64 位版本的 Microsoft Windows 上设置 32 位 DSN

在 64 位版本的 Windows 上,ODBC 数据源管理器在默认情况下会创建并编辑与 64 位 Vertica ODBC 库关联的 DSN。

尝试将这些 64 位 DSN 与 32 位客户端应用程序结合使用会生成架构不匹配错误。相反,您必须通过运行 32 位 ODBC 管理器为 32 位客户端创建特定的 32 位 DSN,该管理器通常位于以下位置:

c:\Windows\SysWOW64\odbcad32.exe

此管理器窗口可用于编辑与 32 位 ODBC 库关联的一组 DSN。使用此版本的 ODBC 管理器创建的 DSN 可与 32 位客户端应用程序结合使用。

2.2.1.4.4.2.2 - ODBC DSN 的密码加密

当您安装 ODBC 驱动程序并创建数据源名称 (DSN) 时,DSN 设置(其中包括)将存储在注册表中。ODBC DSN 的密码加密仅适用于 Windows 系统。

对 ODBC 数据源名称 (DSN) 的密码进行加密,可防止未经授权的数据库访问。密码默认不加密,以纯文本形式存储。

启用密码加密

使用 EncryptPassword 参数,可为 ODBC DSN 启用或禁用密码加密:

  • EncryptPassword = true 启用密码加密

  • EncryptPassword = false (默认)禁用密码加密

在 Windows 注册表中设置 EncryptPassword - HKEY_LOCAL_MACHINE > Software > Vertica > ODBC > Driver EncryptPassword=<true/false>

加密密码在以下注册表位置更新:

对于用户 DSN:

HKEY_CURRENT_USER-> Software -> ODBC -> ODBC.INI -> DSNNAME -> PWD

对于系统 DSN:

HKEY_LOCAL_MACHINE-> Software -> ODBC -> ODBC.INI -> DSNNAME -> PWD

验证密码加密

使用 Windows 注册表编辑器,根据 EncryptPassword 的值确定是否已启用密码加密。根据您安装的 DSN 类型,检查以下内容:

对于用户 DSN: HKEY_CURRENT_USER > Software > ODBC > ODBC.INI > dsn name > isPasswordEncrypted=<1/0>

对于系统 DSN: HKEY_LOCAL_MACHINE > Software > ODBC > ODBC.INI > dsn name > isPasswordEncrypted=<1/0>

对于每个 DSN,isPasswordEncrypted 参数值表示密码加密的状态。其中,1 表示密码已加密,0 表示密码未加密。

2.2.1.4.4.2.3 - 使用 Excel 测试 ODBC DSN

可以使用 Microsoft Excel 验证应用程序是否能够连接到 ODBC 数据源或其他 ODBC 应用程序。

  1. 打开 Microsoft Excel,然后选择数据 (Data) > 获取外部数据 (Get External Data) > 来自其他来源 (From Other Sources) > 来自 Microsoft Query (From Microsoft Query)

  2. 当“选择数据源 (Choose Data Source)”对话框打开之后:

    1. 请选择新建数据源 (New Data Source),然后单击确定 (OK)

      1. 输入数据源的名称。

      2. 选择 Vertica 驱动程序。

      3. 单击连接

  3. 当“Vertica 连接对话框 (Vertica Connection Dialog)”打开之后,输入 DSN 的连接信息,然后单击确定 (OK)

  4. 在“创建新数据源”对话框上单击确定,以返回到“选择数据源”对话框。

  5. 选择“VMart_Schema*”,并验证是否已取消选中“使用查询向导 (Use the Query Wizard)”复选框。单击确定 (OK)

  6. 当“添加表”对话框打开后,单击关闭

  7. 当“Microsoft 查询 (Microsoft Query)”窗口打开之后,单击 SQL 按钮。

  8. 在“SQL”窗口中,编写任意简单查询以测试连接。例如:

    SELECT DISTINCT calendar_year FROM date_dimension;
    
  9. *如果您看到提醒“SQL Query 无法以图形表示。是否继续?(SQL Query can't be represented graphically. Continue anyway?)”,请单击**确定 (OK)**。      *数据值 2003、2004、2005、2006、2007 表示已成功连接到 ODBC,并通过 ODBC 执行了查询。
    
  10. 选择文件 (File) > 将数据返回到 Microsoft Office Excel (Return Data to Microsoft Office Excel)

  11. 在“导入数据”对话框中,单击确定

    数据当前在 Excel 工作表中可用。

2.2.1.4.4.3 - 为 macOS 客户端创建 ODBC DSN

可以使用 Vertica ODBC 驱动程序设置 ODBC DSN。此过程假设您已按照安装 ODBC 客户端驱动程序中所述安装了驱动程序。

设置 DSN

  1. 使用 Web 浏览器下载并安装 Apple ODBC Administrator Tool

  2. 安装之后,找到并打开 ODBC 管理员工具:

    1. 导航至 Finder (查找器) > 应用程序 (Applications) > 实用程序 (Utilities)

    2. 打开 ODBC 管理员工具。

  3. 单击驱动程序 (Drivers) 选项卡,然后验证是否已安装 Vertica 驱动程序。

  4. 指定是否希望客户端系统上的所有用户都能访问 Vertica 数据库的 DSN:

    • 如果想让所有用户都可访问,则单击系统 DSN (System DSN) 选项卡。

    • 否则,单击用户 DSN (User DSN) 选项卡以创建仅供您的 Macintosh 用户帐户使用的 DSN。

  5. 单击添加... (Add...) 创建用于连接到 Vertica 数据库的新的 DSN。

  6. 滚动浏览“选择驱动程序 (Choose A Driver)”对话框中的驱动程序列表以查找 Vertica 驱动程序。选择该驱动程序,然后单击确定 (OK)。此时将打开一个对话框,请求提供 DSN 参数信息。

  7. 在对话框中,输入数据源名称 (DSN) 和可选的 描述 (Description)。为此,请单击 添加 (Add) 以插入关键字(参数)和定义连接到数据库所需设置的值,其中包括数据库名称、服务器主机、数据库用户名(例如 dbaadmin)、数据库密码和端口。然后单击确定 (OK).

  8. 在“ODBC Administrator”对话框中,单击应用 (Apply)

    有关包括 Vertica 特定参数的完整参数列表,请参阅 ODBC DSN 连接属性

配置 ODBC 管理员工具之后,您可能需要配置其他驱动程序设置才能使用 DSN,具体取决于环境。有关详细信息,请参阅其他 ODBC 驱动程序配置设置

2.2.1.4.4.3.1 - 使用 iodbctest 测试 ODBC DSN

Mac OS X 上的标准 iODBC 驱动程序管理器包含一个名为 iodbctest 的实用程序,您可以使用此实用程序测试 DSN,以验证该 DSN 是否配置正确。应向此命令传递与用于打开 ODBC 数据库连接的连接字符串格式相同的连接字符串。配置 DSN 连接之后,您可以运行查询以验证该连接是否正常工作。

例如:


# iodbctest "DSN=VerticaDSN;UID=dbadmin;PWD=password"
iODBC Demonstration program
This program shows an interactive SQL processor
Driver Manager: 03.52.0607.1008
Driver: 07.01.0200 (verticaodbcw.so)
SQL> SELECT table_name FROM tables;
table_name
--------------------------------------------------------------------------------------------------------------------------------
customer_dimension
product_dimension
promotion_dimension
date_dimension
vendor_dimension
employee_dimension
shipping_dimension
warehouse_dimension
inventory_fact
store_dimension
store_sales_fact
store_orders_fact
online_page_dimension
call_center_dimension
online_sales_fact
numbers
result set 1 returned 16 rows.

2.2.1.4.4.4 - ODBC DSN 连接属性

下表列出了可以在 DSN 中设置以与 Vertica ODBC 驱动程序结合使用的连接属性。要设置这些参数,请参阅 设置 DSN 连接属性

所需的连接属性

若要创建正常工作的 DSN,您至少需要设置这些连接属性。

可选属性

高级设置

标识

OAuth 连接属性

以下连接属性与 ODBC 中的 OAuth 相关。

加密

第三方兼容性

Kerberos 连接属性

可以将以下属性用于使用 Kerberos 的客户端身份验证。

另请参阅

ODBC 驱动程序设置

2.2.1.4.4.5 - 设置 DSN 连接属性

以下表中的属性是所有用户 DSN 条目和所有系统 DSN 条目共用的。所提供的示例适用于 Windows 客户端。

要编辑 DSN 属性:

  • 在 UNIX 和 Linux 客户端平台上,您可以编辑 odbc.ini 文件。此文件的位置特定于驱动程序管理器。请参阅为 Linux 创建 ODBC DSN

  • 在 Windows 客户端平台上,您可以使用 Vertica ODBC 客户端驱动程序界面编辑部分 DSN 属性。请参阅为 Windows 客户端创建 ODBC DSN

  • 您也可以直接编辑 DSN 属性,方法是在 Windows 注册表中(例如在 HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI\\DSNname 中)打开 DSN 条目。直接编辑注册表会带来风险,因此,只应对无法通过 ODBC 驱动程序用户界面或客户端代码进行设置的属性使用此方法。

  • 当使用 SQLDriverConnect() 函数打开连接时,您可以在连接字符串中设置属性:

    sqlRet = SQLDriverConnect(sql_hDBC, 0, (SQLCHAR*)"DSN=DSNName;Locale=en_GB@collation=binary", SQL_NTS, szDNS, 1024,&nSize, SQL_DRIVER_NOPROMPT);
    
  • 已使用 SQLGetConnectAttr()SQLGetStmtAttr() API 调用建立与 Vertica 的连接后,客户端代码可以检索 DSN 属性值。可以使用 SQLSetConnectAttr()SQLSetStmtAttr() 设置部分属性。

    有关特定于 Vertica 的属性的列表,请参阅特定于 Vertica 的 ODBC 头文件

2.2.1.4.5 - ODBC 驱动程序设置

  • DriverManagerEncoding:驱动程序管理器使用的 UTF 编码标准。可以是以下内容之一:

    • UTF-8

    • UTF-16

    • UTF-32

    ODBC 驱动程序编码必须与驱动程序管理器的编码相匹配。下表列出了不设置该参数时生效的各种平台的默认编码。如果默认值与驱动程序管理器使用的编码不匹配,则必须进行手动设置。有关驱动程序管理器编码的详细信息,请参阅驱动程序管理器文档。

  • ErrorMessagesPath:必需,包含 ODBC 驱动程序的错误消息文件的目录路径。这些文件(ODBCMessages.xmlVerticaMessages.xml)存储在 Vertica ODBC 驱动程序文件所在的目录(例如,已下载的 .tar 所在的 opt/vertica/en-US)中。

  • ODBCInstLib:ODBC 安装程序库的路径。只有当未在 LD_LIBRARY_PATHLIB_PATH 环境变量中设置包含该库的目录时,才需要此设置。主要驱动程序管理器的库文件如下:

    • UnixODBC: libodbcinst.so

    • iODBC: libiodbcinst.so (在 macOS 上为 libiodbcinst.2.dylib

    • DataDirect: libodbcinst.so

您还可以控制 ODBC 和 ADO.NET 的客户端-服务器消息日志记录。有关详细信息,请参阅配置 ODBC 日志

Linux 和 macOS

要在 Linux 或 macOS 上设置这些参数:

  1. 在客户端系统的任何位置创建文件 vertica.ini。公共位置在共享配置的 /etc/ 中,或者在每个用户配置的主目录中。

  2. 验证 ODBC 驱动程序的用户是否具有文件的读取权限。

  3. VERTICAINI 环境变量设置为 vertica.ini 的路径。例如:

    $ export VERTICAINI=/etc/vertica.ini
    
  4. vertica.ini 中创建名为 [Driver] 的部分:

    [Driver]
    
  5. [Driver] 下,按如下格式设置参数。每个参数必须有其自己的行:

    [Driver]
    DriverManagerEncoding=UTF-16
    ODBCInstLib=/usr/lib64/libodbcinst.so
    

Windows

Windows 客户端驱动程序安装程序自动为 ODBC 驱动程序配置所有必要的设置。设置存储在注册表中的 HKEY_LOCAL_MACHINE\SOFTWARE\Vertica\ODBC\Driver 下。

如果要进一步配置 ODBC,请使用 ODBC 数据源 程序。

2.2.1.4.6 - 配置 ODBC 日志

以下参数控制 ODBC 客户端驱动程序是否以及如何记录客户端和服务器之间的消息。

设置这些参数的方式因操作系统不同而异:

  • 在 Linux 和 macOS 上,编辑您在安装期间创建的 vertica.ini。例如,要将所有警告和更严重的消息记录到 /tmp/ 中的日志文件:

    [Driver]
    LogLevel=4
    LogPath=/tmp
    
  • 在 Windows 上的 Windows 注册表中,编辑 HKEY_LOCAL_MACHINE\SOFTWARE\Vertica\ODBC\Driver 下面的键。

参数

  • LogLevel:在客户端和服务器之间记录的消息的严重性。有效值包括:

    • 0:无日志记录

    • 1:严重错误

    • 2:错误

    • 3:警告

    • 4:信息

    • 5:Debug

    • 6:跟踪(所有消息)

    为此设置指定的值设置了要记录的消息的最低严重性。例如,如果将 LogLevel 设置为 3,则表示客户端驱动程序将记录所有警告、错误和严重错误。

  • LogPath:用于存储日志文件的目录的绝对路径。例如: /var/log/verticaodbc

将日志条目转移到 ETW (Windows)

在 Windows 客户端上,可以将 ODBC 日志条目发送到 Windows 事件跟踪 (ETW),以便它们显示在 Windows 事件查看器中:

  • 将驱动程序注册为 Windows 事件日志提供程序,并启用日志。

  • 通过将带有数据 ETW 的字符串值 LogType 添加到 Windows 注册表来激活 ETW。

  • 了解 Vertica 如何压缩 Windows 事件查看器的日志级别。

  • 了解在事件查看器中的何处可以找到日志。

  • 了解日志条目中事件 ID 的含义。

将 ODBC 驱动程序注册为 Windows 事件日志提供程序

要使用 ETW 日志记录,必须将 ODBC 驱动程序注册为 Windows 事件日志提供程序。您可以选择注册 32 位或 64 位驱动程序。注册驱动程序后,必须启用日志。

  1. 以管理员身份打开命令提示符窗口,或使用“以管理员身份运行 (Run as Administrator)”选项启动命令提示符。

  2. 运行命令 wevtutil im 以注册 32 位或 64 位版本的驱动程序。

    • 对于 64 位 ODBC 驱动程序,运行:

      wevtutil im "c:\Program Files\Vertica Systems\ODBC64\lib\VerticaODBC64.man"
      /resourceFilePath:"c:\Program Files\Vertica Systems\ODBC64\lib\vertica_9.1_odbc_3.5.dll"
      /messageFilePath:"c:\Program Files\Vertica Systems\ODBC64\lib\vertica_9.1_odbc_3.5.dll"
      
    • 对于 32 位 ODBC 驱动程序,运行:

      wevtutil im "c:\Program Files (x86)\Vertica Systems\ODBC32\lib\VerticaODBC32.man"
      /resourceFilePath:"c:\Program Files (x86)\Vertica Systems\ODBC32\lib\vertica_9.1_odbc_3.5.dll"
      /messageFilePath:"c:\Program Files (x86)\Vertica Systems\ODBC32\lib\vertica_9.1_odbc_3.5.dll"
      
  3. 运行命令 wevtutil sl 以启用日志。

    • 对于 64 位 ODBC 驱动程序日志,运行:

      wevtutil sl VerticaODBC64/e:true
      
    • 对于 32 位 ODBC 驱动程序日志,运行:

      wevtutil sl VerticaODBC32/e:true
      

添加字符串值 LogType

默认情况下,Vertica 不会将 ODBC 日志条目发送到 ETW。要激活 ETW,请将字符串 LogType 添加到 Windows 注册表,并将其值设置为 ETW

  1. 通过在 Windows“运行 (Run)”命令框中键入 regedit.exe 来启动注册表编辑器。

  2. 导航到注册表中的 HKEY_LOCAL_MACHINE\SOFTWARE\Vertica\ODBC\Driver

  3. 注册表编辑器 (Registry Editor) 窗口的右窗格中右键单击。

  4. 选择新建 (New),然后选择字符串值 (String Value)

  5. 将字符串值的名称从 New Value #1 更改为 LogType

  6. 双击新的 LogType 条目。当系统提示输入新值时,输入 ETW

  7. 退出注册表编辑器。

默认情况下,禁用 ETW。启用 ETW 后,您可以通过从 LogType 字符串中清除值 ETW 来禁用它。

虽然 LogLevel 的范围从 0 到 6,但对于 Windows 事件查看器,此范围被压缩到 0 到 3 的范围。

以下示例显示了在 Windows 事件查看器中显示时如何转换 LogLevel。

  • LogLevel 为 5 将致命错误、错误、警告、信息和调试日志级别条目作为级别 4(信息)发送到事件查看器。

  • LogLevel 为 6 将致命错误、错误、警告、调试和跟踪日志级别条目作为级别 4 发送到事件查看器。

在事件查看器中查找日志

  1. 启动 Windows 事件查看器

  2. 事件查看器(本地) (Event Viewer (Local)),展开应用程序和服务日志 (Applications and Services Logs)

  3. 展开包含您要查看的日志的文件夹(例如 VerticaODBC64)。

  4. 选择文件夹下的 Vertica ODBC 日志。条目出现在右窗格中。

  5. 请注意事件 ID (Event ID) 字段中的值。每个事件日志条目包含四个事件 ID 之一:

    • 0:信息性(调试、信息和跟踪事件)

    • 1:错误

    • 2:严重事件

    • 3:警告

2.2.1.5 - Python 客户端驱动程序

Vertica 支持多个用于创建客户端应用程序的 Python 驱动程序。

先决条件

要创建 Python 客户端应用程序,您必须安装所需的驱动程序。

2.2.1.5.1 - 安装 Python 客户端驱动程序

Vertica 支持多个 Python 客户端驱动程序。

安装 Vertica-Python

有关安装和使用说明,请参阅 Vertica-Python 存储库。

安装 Pyodbc

Pyodbc 模块与 Vertica ODBC 客户端驱动程序交互。要安装它:

  1. 安装 ODBC 客户端驱动程序

  2. 安装兼容版本PythonPyodbc

2.2.1.6 - Node.js 客户端驱动程序

开源 vertica-nodejs 客户端驱动程序允许您使用 JavaScript 与数据库交互。有关详细信息,请参阅 npm 上的 vertica-nodejs 包

2.2.1.7 - OLE DB 客户端驱动程序

OLE DB 客户端驱动程序是 Microsoft Analysis Services (SSAS)C# 客户端应用程序与 Vertica 数据库交互的接口。

2.2.1.7.1 - 安装 OLE DB 客户端驱动程序

要安装 Vertica OLE DB 客户端驱动程序:

  1. 下载 Windows 客户端驱动安装程序。有关此安装程序中包含的驱动程序的详细信息,请参阅 Windows 客户端驱动程序安装程序

  2. 运行安装程序并按照提示安装驱动程序。

  3. 重新启动系统。

安装 OLE DB 客户端驱动程序后,您可以配置 ETW 日志记录

有关 OLE DB 客户端驱动程序如何与其他 Microsoft 组件集成的详细信息,请参阅 Microsoft 组件配置

有关连接属性的列表,请参阅 OLE DB 连接属性

2.2.1.7.1.1 - OLE DB 连接属性

可以使用“连接管理器 (Connection Manager)”设置 OLE DB 连接字符串属性,这些属性定义了连接。可以从 Visual Studio 中访问“连接管理器 (Connection Manager)”。

这些连接参数会显示在“连接 (Connection)”页面上。

连接管理器 (Connection Manager) 对话框中的 全部 (All) 页面列出了提供程序的所有可能的连接字符串属性。

下表列出了全部 (All) 页面中的连接参数。

有关特定于 Microsoft 的 OLE DB 属性,请参阅 Microsoft 文档 [OLE DB Properties](https://msdn.microsoft.com/en-us/library/windows/desktop/ms723130(v=vs.85)

2.2.1.7.1.2 - 配置 OLE DB 日志

以下参数控制 OLE DB 客户端驱动程序如何记录客户端和服务器之间的消息。要设置它们,请编辑 Windows 注册表中 HKEY_LOCAL_MACHINE\SOFTWARE\Vertica\OLEDB\Driver 下的密钥:

  • LogLevel:在客户端和服务器之间记录的消息的严重性。有效值包括:

    • 0:无日志记录

    • 1:严重错误

    • 2:错误

    • 3:警告

    • 4:信息

    • 5:调试

    • 6:跟踪(所有消息)

    为此设置指定的值设置了要记录的消息的最低严重性。例如,如果将 LogLevel 设置为 3,则表示客户端驱动程序将记录所有警告、错误和严重错误。

  • LogPath:用于存储日志文件的目录的绝对路径。例如: /var/log/verticaoledb

将 OLE DB 日志条目转移到 ETW

在 Windows 客户端上,您可以指示 Vertica 将 OLE DB 日志条目发送到 Windows 事件跟踪 (ETW)。设置后,OLE DB 日志条目将出现在 Windows 事件查看器中。要使用 ETW:

  • 将驱动程序注册为 Windows 事件日志提供程序,并启用日志。

  • 通过将字符串值添加到 Windows 注册表来激活 ETW。

  • 了解 Vertica 如何压缩 Windows 事件查看器的日志级别。

  • 了解在事件查看器中的何处可以找到日志。

  • 了解日志条目中事件 ID 的含义。

将 OLE DB 驱动程序注册为 Windows 事件日志提供程序

要使用 ETW 日志记录,您必须将 OLE DB 驱动程序注册为 Windows 事件日志提供程序。您可以选择注册 32 位或 64 位驱动程序。注册驱动程序后,您必须启用日志。

  1. 以管理员身份打开命令提示符窗口,或使用“以管理员身份运行 (Run as Administrator)”选项启动命令提示符。

  2. 运行命令 wevtutil im 以注册 32 位或 64 位版本的驱动程序。

    1. 对于 64 位 OLE DB 驱动程序,运行:

      wevtutil im "c:\Program Files\Vertica Systems\OLEDB64\lib\VerticaOLEDB64.man"
      /resourceFilePath:"c:\Program Files\Vertica Systems\OLEDB64\lib\vertica_8.1_oledb.dll"
      /messageFilePath:"c:\Program Files\Vertica Systems\OLEDB64\lib\vertica_8.1_oledb.dll"
      
    2. 对于 32 位 OLE DB 驱动程序,运行:

      wevtutil im "c:\Program Files (x86)\Vertica Systems\OLEDB32\lib\VerticaOLEDB32.man"
      /resourceFilePath:"c:\Program Files (x86)\Vertica Systems\OLEDB32\lib\vertica_8.1_oledb.dll"
      /messageFilePath:"c:\Program Files (x86)\Vertica Systems\OLEDB32\lib\vertica_8.1_oledb.dll"
      
  3. 运行命令 wevtutil sl 以启用日志。

    1. 对于 64 位 OLE DB 驱动程序日志,运行:

      wevtutil sl VerticaOLEDB64/e:true
      
    2. 对于 32 位 ODBC 驱动程序日志,运行:

      wevtutil sl VerticaOLEDB32/e:true
      

添加字符串值 LogType

默认情况下,Vertica 不会将 OLE DB 日志条目发送到 ETW。要激活 ETW,请将字符串 LogType 添加到 Windows 注册表,并将其值设置为 ETW

  1. 通过在 Windows“运行 (Run)”命令框中键入 regedit.exe 来启动注册表编辑器。

  2. 在注册表中,导航到:HKEY_LOCAL_MACHINE\SOFTWARE\Vertica\OLEDB\Driver

  3. 注册表编辑器 (Registry Editor) 窗口的右窗格中右键单击。

  4. 选择新建 (New),然后选择字符串值 (String Value)

  5. 将字符串值的名称从 New Value #1 更改为 LogType

  6. 双击新的 LogType 条目。当系统提示输入新值时,输入 ETW

  7. 退出注册表编辑器。

默认情况下,ETW 设置为 off。激活 ETW 后,您可以稍后通过从 LogType 字符串中清除值 ETW 将其关闭。

虽然 LogLevel 的范围从 0 到 6,但对于 Windows 事件查看器,此范围被压缩到 0 到 3 的范围。

以下示例显示了在 Windows 事件查看器中显示时如何转换 LogLevel。

  • LogLevel 为 5 将致命错误、错误、警告、信息和调试日志级别条目作为级别 4(信息)发送到事件查看器。

  • LogLevel 为 6 将致命错误、错误、警告、调试和跟踪日志级别条目作为级别 4 发送到事件查看器。

在事件查看器中查找日志

  1. 启动 Windows 事件查看器

  2. 事件查看器(本地) (Event Viewer (Local)),展开应用程序和服务日志 (Applications and Services Logs)

  3. 展开包含您要查看的日志的文件夹(例如 VerticaOLEDB64)。

  4. 选择文件夹下的 Vertica ODBC 日志。条目出现在右窗格中。

  5. 请注意事件 ID (Event ID) 字段中的值。每个事件日志条目包含四个事件 ID 之一:

    • 0:信息性(调试、信息和跟踪事件)

    • 1:错误

    • 2:严重事件

    • 3:警告

2.2.1.7.2 - 适用于 Windows 的 Microsoft Connectivity Pack

适用于 Windows 的 Vertica Microsoft Connectivity Pack 提供了一个用于访问 Microsoft Business Intelligence 工具的配置文件。Connectivity Pack 作为 Windows 客户端驱动程序和工具的一部分安装。

有关 Microsoft Connectivity Pack 中包含的组件的详细信息,请参阅 Microsoft 组件

2.2.1.7.2.1 - Microsoft 组件

本节介绍可与 Microsoft Visual Studio 和 Microsoft SQL Server 结合使用的 Microsoft Business Intelligence 组件。配置后,您可以使用这些 Microsoft 组件来开发使用 Vertica 服务器的业务解决方案。

2.2.1.7.2.1.1 - Microsoft 组件配置

使用 Vertica ADO.NET 驱动程序、Visual Studio 插件和 OLE DB 驱动程序,您可以将 Vertica 服务器与包含先前已安装在系统上的 Microsoft 组件的环境集成。还有其他工具可用于与 Microsoft SQL Server 集成。

可用的驱动程序提供了与以下 Microsoft 组件的集成:

  • 适用于 Visual Studio 2008 的 Business Intelligence Development Studio (BIDS),与 SQL Server 2012 结合使用。BIDS 是一个基于客户端的应用程序,用于开发基于 Microsoft Visual Studio 开发环境的商业智能解决方案。此应用程序包含其他特定于 SQL Server Business Intelligence 的项目类型。作为开发人员,您可以使用 BIDS 开发业务解决方案。

  • 适用于 Visual Studio 2008/2010/2012/2013/2015 的 SQL Server Data Tools - Business Intelligence (SSDT-BI),与 SQL Server 2012、SQL Server 2014 和 SQL Server 2016 结合使用。SSDT-BI 取代适用于 Visual Studio 2008、Visual Studio 2010、Visual Studio 2012、Visual Studio 2013 和 Visual Studio 2015 的 BIDS。此组件的用途与 BIDS 相同,即提供用于开发商业智能解决方案的开发环境。

  • 适用于 SQL Server 2012、SQL Server 2014 和 SQL Server 2016 的 SQL Server Analysis Services (SSAS)。可以使用 SSAS 进行 OLAP 和数据挖掘,同时使用 Vertica 作为多维数据集创建的源。

  • 适用于 SQL Server 2012、SQL Server 2014 和 SQL Server 2016 的 SQL Server Integration Services (SSIS)。SSIS 提供了 SQL Server 类型映射,用于在 Vertica 和 SQL Server 之间映射数据类型。可以将 SSIS 用于数据迁移、数据集成和工作流以及 ETL。

下图显示了 Microsoft 组件和 Vertica 依赖项之间的关系。

2.2.1.7.2.1.2 - BIDS 和 SSDT-BI

Microsoft Visual Studio 2008 中提供 Business Intelligence Development Studio (BIDS) 和其他特定于 SQL Server Business Intelligence 的项目类型。BIDS 是用于开发包括分析服务、集成服务和报告服务项目在内的业务解决方案的主要环境。

SQL Server Data Tools - Business Intelligence (SSDT-BI) 取代适用于 Visual Studio 2010、2012、2013 和 2015 的 BIDS。此组件的用途与 BIDS 相同,即提供用于开发业务解决方案的开发环境。

BIDS 和 SSDT-BI 都是基于客户端的应用程序,并且都包含其他特定于 SQL Server Business Intelligence 的项目类型。

可以使用 Visual Studio Shell 集成插件从 Visual Studio Server Explorer 中浏览数据库。使用此功能,您可以在 BIDS 或 SSDT-BI 开发环境外部工作,以执行诸如列出表或插入数据等任务。在 BIDS 或 SSDT-BI 模式下使用 Visual Studio 时,您可以使用 Vertica 数据库中的数据开发业务解决方案。例如,您可以创建多维数据集或打开表。

Microsoft 不支持以下配置:

  • 不能将 Microsoft Visual Studio 2008 与 BIDS 开发环境结合使用以创建 SQL Server 2012 Business Intelligence 解决方案。

  • 不能将 Microsoft Visual Studio 2010/2012/2013/2015 与 SSDT-BI 开发环境结合使用以创建 SQL Server 2008 Business Intelligence 解决方案。

2.2.1.7.2.1.3 - SQL Server Analysis Services (SSAS) 支持

BIDS 或 SSDT-BI 包含 Analysis Services 项目,该项目可用于开发适用于商业智能应用程序的联机分析处理 (Online Analytical Processing, OLAP)。此项目类型包含以下内容的模板:

  • 多维数据集

  • 维度

  • 数据源

  • 数据源视图

它还提供处理这些对象的工具。

可以在 OLE DB 连接属性 中找到 OLE DB 连接属性。

2.2.1.7.2.1.4 - SQL Server Integration Services (SSIS) 支持

BIDS 或 SSDT-BI 包含用于开发 ETL 解决方案的 Integration Services 项目。此项目类型包含以下内容的模板:

  • 数据源

  • 数据源视图

它还提供处理这些对象的工具。

通过 SSIS 和导入/导出向导,您可以使用 Vertica 作为数据源和数据目标。必须在集成服务器和 BIDS 工作站或 SSDT-BI 工作站上安装特定于 Vertica 的映射文件才能启用此功能。适用于 Windows 的 Vertica 客户端驱动程序和工具将以 32 位和 64 位版本安装这些映射文件作为“SQL Server 类型映射”组件。

2.2.1.7.2.1.5 - SQL Server Reporting Services (SSRS) 支持

BIDS 或 SSDT-BI 包含用于开发报表解决方案的报表项目。

可以使用 Vertica 作为 Reporting Services 的数据源。安装程序将实施各种配置文件修改,以在 BIDS 工作站或 SSDT-BI 工作站和 Reporting Services 服务器上启用此功能。

2.2.1.7.2.2 - 兼容性问题和限制

本节列出兼容性问题以及将 Microsoft Connectivity Pack 与 Microsoft Visual Studio 和 Microsoft SQL Server 集成的限制。

2.2.1.7.2.2.1 - BIDS 和 SSDT-BI 限制

BIDS 和 SSDT-BI 是适用于 Analysis Services、Integration Services 和 Reporting Services 项目的 32 位开发环境。它们并非设计用于在 64 位 Itanium 架构上运行,因此不会安装在 Itanium 服务器上。

2.2.1.7.2.2.2 - SSAS 限制

  • 不支持 SSAS 表格模型。

  • 安装 Vertica OLE DB 驱动程序后,如果 SSAS 多维数据集构建失败,请重新启动 SSAS 服务。

2.2.1.7.2.2.3 - SSIS 数据类型限制

以下部分介绍了使用 SQL Server Integration Services (SSIS) 时的数据类型限制。

时间数据传输

传输时间数据时,SSIS 使用支持超过六位数精度的 TimeSpan 数据类型。Vertica ADO.NET 驱动程序会将 TimeSpan 转换为最多支持六位数的 Interval 数据类型。传输过程中,Interval 类型不会转换为 TimeSpan 类型。因此,如果时间值的精度超过六位数,则数据会被截断(而不是舍入)。

有关 ADO.NET 数据类型的信息,请参考 ADO.NET 数据类型

DATE 和 DATETIME 精度

为确保正常运行而不发生错误,DATE 和 DATETIME 的范围介于 0001-01-01 00:00:00.0000000 到 9999-12-31 23:59:59.999999 之间。

在 SSIS 中,DATETIME 类型 (DT_TIMESTAMP) 对秒最多支持三个小数位。系统会自动丢弃后面的小数。只能对介于 January 1, 1753 到 December 31, 9999 之间的 DATETIME 值执行派生列转换。

数值精度

允许的最大和最小十进制值为:

  • 最大值: +79,228,162,514,264,337,593,543,950,335

  • 最小值: -79,228,162,514,264,337,593,543,950,335

例如,如果位数是 16,值的范围为:

+/- 7,922,816,251,426.4337593543950335

有效位数范围为小于 29 以及大于 38 的任何数字。使用 29 和 38 之间的小数位数不会生成错误。

请参阅:http://msdn.microsoft.com/en-us/library/system.decimal.maxvalue.aspx

不支持的浮点值

SQL Server 不支持 NaN、Infinity 或 –Infinity 值。使用 SSIS 在 Vertica 实例之间进行传输时,可以使用这些值,但这些值不能用于 SQL Server 目标。

字符串转换

在 SSIS 中使用的 CHAR 和 VARCHAR 数据类型为 DT_WSTR,其最大长度是 4000 个字符。

在 SSIS 中,Vertica 字符串会转换为 SSIS 中的 Unicode 字符串,以处理多语言数据。可以使用数据转换任务将这些字符串转换为 ASCII。

标度

只要所使用的小数位数超过 38,SSIS 就会将其替换为值 4。

区间转换

SSIS 不支持间隔类型。它会将这些类型转换为 TIME,并去除日组件。时间间隔类型超过一天的任何包将返回不正确的结果。

SQL Server 导入和导出向导的数据映射问题

使用 SQL Server 导入和导出向导创建集成服务包 (SSIS) 时,某些数据类型无法自动正确映射。将该向导与以下提供程序结合使用时,将出现映射问题:

  • 适用于 SQL Server 2008 或 SQL Server 2012 的 SQL Server Native OLE DB 提供程序

  • 适用于 SQL Server 2010/2012 的 SQL Server Native Client 10.0/11.0 提供程序

要避免此问题,请使用 BIDS 或 SSDT-BI 手动更改类型映射。

数据传输失败

将集成服务包 (SSIS) 与适用于 SQL Server 2008 或 SQL Server 2012 的 SQL Server OLE DB 提供程序结合使用时,如果是从 Vertica 传输到 SQL Server,则某些数据类型的传输会失败。若要避免此问题,请使用 BIDS 或 SSDT-BI 传输数据。

VARBINARY/LONG VARBINARY 数据类型的批量插入

有时,VARBINARY 或 LONG VARBINARY 数据类型的批量插入的行之一会超过数据类型限制:

  • VARBINARY:65 KB

  • LONG VARBINARY:32 MB

在这种情况下,将拒绝所有行,而非仅拒绝长度超过类型限制的行。批量插入将失败,并显示消息“行被拒绝 (row(s) are rejected)”。

若要避免此问题,请使用谓词从源中筛选出无法适应接收数据库的行。

SQL Server 查询设计器布尔查询

在 SQL Server 查询设计器中发出布尔查询时,必须用引号将布尔列值括起来。否则,您将收到 SQL 执行错误(例如,someboolean = 'true')。

2.2.1.7.2.2.4 - SSRS 限制

数据连接向导解决方法

SSRS 报表向导提供一个数据连接向导。选择该向导并输入所有连接信息后,确定 按钮被禁用。您无法保存工作,也无法继续操作。解决方法是不要使用该向导,并改为使用以下面板:

报表向导 - 查询设计器

Vertica 使用报表向导的通用查询设计器。其他数据源使用支持以可视化方式构建查询的图形查询设计器。图形查询设计器是名为 Visual Data Tools (VDT) 的程序包的一部分。图形查询设计器只能与通用 OLE DB 提供程序和内置提供程序配合工作。您不能将其与 Vertica 数据提供程序结合使用。

报表生成器

Report Builder 是一款基于 Web 的报告设计工具。此工具不支持使用自定义数据扩展来创建报表,因此您不能将其与 Vertica 结合使用。使用报表生成器创建报表时,现有 Vertica 数据源会显示在可用数据源的列表中。但是,选择 Vertica 数据源会导致发生错误。

映射 Vertica ** 目标时未自动提供架构名称**

目前,当您映射 Vertica 目标时,不会自动提供架构名称。您必须手动输入架构名称或从下拉菜单中选择架构名称,如下所示:

2.2.1.8 - ADO.NET 客户端驱动程序

Vertica ADO.NET 驱动程序允许您使用 C# 访问 Vertica

2.2.1.8.1 - 安装 ADO.NET 客户端驱动程序

先决条件

ADO.NET 客户端驱动程序需要以下条件:

  • 支持的操作系统

  • 至少 512MB 内存

  • .NET Framework 的支持版本。如果您不满足此要求,在大多数情况下,安装程序会主动为您安装。

安装

要安装 ADO.NET 客户端驱动程序:

  1. 下载 Windows 客户端驱动安装程序。有关此安装程序中包含的驱动程序的详细信息,请参阅 Windows 客户端驱动程序安装程序

  2. 运行安装程序并按照提示安装驱动程序。

  3. 重新启动系统。

2.2.2 - 升级客户端驱动程序

通常会为 Vertica 服务器的每个新发行版更新 Vertica 客户端驱动程序。客户端驱动程序安装包包含相应 Vertica 服务器发行版的版本号。通常,驱动程序与下一个发行版具有向前兼容性,因此在升级到 Vertica Analytics Platform 服务器的下一个版本之后,客户端应用程序仍能使用较旧版本的驱动程序进行连接。有关哪些客户端驱动程序版本适用于每个 Vertica 服务器版本的详细信息,请参阅客户端驱动程序和服务器版本兼容性

应在升级服务器之后尽快升级客户端,以利用新功能并保持与服务器的最高兼容性。

若要升级驱动程序,请遵循在最初安装驱动程序时使用的相同过程。新安装将覆盖旧安装。有关升级的任何特别说明,请参阅在客户端平台上安装驱动程序的特定说明。

2.2.3 - 设置客户端连接标记

连接到 Vertica 数据库时,您可以设置客户端连接标记。您还可以使用 SET_CLIENT_LABEL 函数设置客户端连接标记,或使用 GET_CLIENT_LABEL 函数返回客户端连接标记。

设置客户端连接标记:

=> SELECT SET_CLIENT_LABEL('py_data_load_application');
               SET_CLIENT_LABEL
----------------------------------------------
 client_label set to py_data_load_application
(1 row)

返回当前的客户端连接标记:


=> SELECT GET_CLIENT_LABEL();
     GET_CLIENT_LABEL
--------------------------
 py_data_load_application
(1 row)

JDBC

JDBC 客户端具有用于设置和返回客户端连接标签的方法:getClientInfo() 和 setClientInfo()。这些方法可与 SQL 函数 GET_CLIENT_LABELSET_CLIENT_LABEL 配合使用。

使用这两种方法时,请确保向 setter 和 getter 方法都传递字符串值 APPLICATIONNAME

setClientInfo() 用于创建客户端标签,而 getClientInfo() 则用于返回客户端标签:

import java.sql.*;
import java.util.Properties;

public class ClientLabelJDBC {

    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "");
        myProp.put("loginTimeout", "35");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                    "jdbc:vertica://docc05.verticacorp.com:5433/doccdb", myProp);
            System.out.println("Connected!");
            conn.setClientInfo("APPLICATIONNAME", "JDBC Client - Data Load");
            System.out.println("New Conn label: " + conn.getClientInfo("APPLICATIONNAME"));
            conn.close();
        } catch (SQLTransientConnectionException connException) {
            // There was a potentially temporary network error
            // Could automatically retry a number of times here, but
            // instead just report error and exit.
            System.out.print("Network connection issue: ");
            System.out.print(connException.getMessage());
            System.out.println(" Try again later!");
            return;
        } catch (SQLInvalidAuthorizationSpecException authException) {
            // Either the username or password was wrong
            System.out.print("Could not log into database: ");
            System.out.print(authException.getMessage());
            System.out.println(" Check the login credentials and try again.");
            return;
        } catch (SQLException e) {
            // Catch-all for other exceptions
            e.printStackTrace();
        }
    }
}

运行此方法时,会将以下结果打印到标准输出:

Connected!
New Conn Label: JDBC Client - Data Load

2.2.4 - 使用旧版驱动程序

Vertica 服务器支持来自以前版本的客户端驱动程序的连接。有关 Vertica 服务器和 Vertica 客户端版本之间兼容性的详细信息,请参阅客户端驱动程序和服务器版本兼容性

2.3 - 访问 Vertica

下表显示了使用受支持的编程语言访问 Vertica 时必须设置的客户端驱动程序:

2.3.1 - C/C++

Vertica 提供开放式数据库连接 (ODBC) 驱动程序,此驱动程序可使应用程序能够连接到 Vertica 数据库。此驱动程序可供自定义编写的客户端应用程序(使用 ODBC API 与 Vertica 交互)使用。ODBC 还可由许多第三方应用程序(包括商业智能应用程序以及提取、转换和加载 (Extract, Transform, and Load, ETL) 应用程序)用来连接到 Vertica。

此部分详细介绍配置 Vertica ODBC 驱动程序的过程。此部分还介绍如何在自己的客户端应用程序中使用 ODBC API 连接到 Vertica。

尽管使用 C、C++、PerlPHP 等编写的客户端应用程序均使用 ODBC 客户端驱动程序连接到 Vertica,但此部分仅阐述 C 和 C++ 应用程序。

2.3.1.1 - ODBC 体系结构

ODBC 架构包含四个层:

  • 客户端应用程序

    此层是一个应用程序,用于通过数据源名称 (DSN) 打开数据源。然后,此应用程序会将请求发送到数据源,并接收这些请求的结果。请求以调用 ODBC 函数的方式发出。

  • 驱动程序管理器

    此层是客户端系统上的一个库,用作客户端应用程序和一个或多个驱动程序之间的中介。该驱动程序管理器执行下列操作:

    • 解析由客户端应用程序提供的 DSN。

    • 加载访问 DSN 中定义的特定数据库所需的驱动程序。

    • 处理来自客户端的 ODBC 函数调用,或将这些调用传递到驱动程序。

    • 从驱动程序检索结果。

    • 在不再需要时卸载驱动程序。

    在 Windows 和 macOS 客户端系统上,驱动程序管理器由操作系统提供。在 Linux 系统上,您通常需要安装驱动程序管理器。有关可在客户端平台上与 Vertica 结合使用的驱动程序管理器的列表,请参阅客户端驱动程序支持

  • Driver

    此层是客户端系统上的一个库,提供对特定数据库的访问。此层可将请求转换为数据库所需的格式,还可将结果重新转换为客户端应用程序所需的格式。

  • 数据库

    数据库可处理在客户端应用程序上启动的请求并返回结果。

2.3.1.2 - ODBC 功能支持

适用于 Vertica 的 ODBC 驱动程序支持 Microsoft ODBC 3.5 规范中定义的大部分功能。以下功能不受支持:

  • 可更新的结果集

  • 向后滚动光标

  • 光标属性

  • 每个连接有多个打开的语句。同时执行的语句必须各自属于不同的连接。例如,当其他语句的结果集处于打开状态时,您无法执行新语句。要使用相同的连接/会话执行另一条语句,请等待当前语句完成执行并关闭其结果集,然后执行新语句。

  • 键集

  • 书签

Vertica ODBC 驱动程序会准确报告其功能。如果需要确定驱动程序是否符合特定功能,您应使用 SQLGetInfo() 函数直接查询驱动程序的功能。

2.3.1.3 - Vertica 和 ODBC 数据类型转换

大多数数据类型均可在 Vertica 和 ODBC 之间透明地进行转换。此部分介绍了几种需要特殊处理的数据类型。

注意

  • GEOMETRY 和 GEOGRAPHY 数据类型被 ODBC 驱动程序视为 LONG VARCHAR 数据。

  • Vertica 支持 ODBC 所支持的标准间隔数据类型。请参阅 Microsoft 的 ODBC 参考文档中的间隔数据类型

  • Vertica 9.0.0 版引入了 UUID 数据类型,包括对 UUID 的 JDBC 支持。Vertica ADO.NET、ODBC 和 OLE DB 客户端在 9.0.1 版中添加了对 UUID 的完全支持。Vertica 保持与不支持 UUID 数据类型的旧 受支持 客户端驱动程序版本的向后兼容性,如下所示:

另请参阅

2.3.1.4 - ODBC 头文件

Vertica ODBC 驱动程序提供了名为 verticaodbc.h 的 C 头文件,该头文件定义了几个可在应用程序中使用的有用常数。使用这些常数,您可以访问和更改特定于 Vertica 的设置。

此文件的位置取决于客户端操作系统:

  • Linux和UNIX系统:/opt/vertica/include
  • Windows系统:C:\Program Files (x86)\Vertica\ODBC\include

下面列出了此文件中定义的常数。

2.3.1.5 - 连接到数据库

在任何 ODBC 应用程序中,第一步总是连接到数据库。创建与使用 ODBC 的数据源的连接时,应使用 DSN(其中包含要使用的驱动程序的详细信息、数据库主机和有关连接到数据源的其他基本信息)的名称。

若要连接到数据库,您需要对应用程序执行以下 4 个步骤:

  1. 调用 SQLAllocHandle() 来为 ODBC 环境分配句柄。此句柄用于创建连接对象和设置应用程序范围设置。

  2. 使用环境句柄设置应用程序要使用的 ODBC 版本。这样可确保数据源知道应用程序将使用哪个 API 与其交互。

  3. 通过调用 SQLAllocHandle() 来分配数据库连接句柄。此句柄代表与特定数据源的连接。

  4. 使用 SQLConnect()SQLDriverConnect() 函数打开与数据库的连接。

创建与数据库的连接时,如果在连接时只需要设置用户名和密码选项,请使用 SQLConnect()。如果要更改诸如区域设置等选项,请使用 SQLDriverConnect()

以下示例演示了使用名为 ExampleDB 的 DSN 连接到数据库。成功创建连接后,此示例仅关闭该连接。

// Demonstrate connecting to Vertica using ODBC.
// Standard i/o library
#include <stdio.h>
#include <stdlib.h>
// Only needed for Windows clients
// #include <windows.h>
// SQL include files that define data types and ODBC API
// functions
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
int main()
{
    SQLRETURN ret;   // Stores return value from ODBC API calls
    SQLHENV hdlEnv;  // Handle for the SQL environment object
    // Allocate an a SQL environment object
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate a handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated an environment handle.\n");
    }

    // Set the ODBC version we are going to use to
    // 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
            (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    if(!SQL_SUCCEEDED(ret)) {
         printf("Could not set application version to ODBC 3.\n");
         exit(EXIT_FAILURE);
    } else {
         printf("Set application version to ODBC 3.\n");
    }
    // Allocate a database handle.
    SQLHDBC hdlDbc;
     ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
     if(!SQL_SUCCEEDED(ret)) {
          printf("Could not allocate database handle.\n");
          exit(EXIT_FAILURE);
     } else {
          printf("Allocated Database handle.\n");
     }
    // Connect to the database using
    // SQL Connect
    printf("Connecting to database.\n");
    const char *dsnName = "ExampleDB";
    const char* userID = "ExampleUser";
    const char* passwd = "password123";
    ret = SQLConnect(hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS,(SQLCHAR*)userID,SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not connect to database.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Connected to database.\n");
    }
    // We're connected. You can do real
    // work here

    // When done, free all of the handles to close them
    // in an orderly fashion.
    printf("Disconnecting and freeing handles.\n");
    ret = SQLDisconnect( hdlDbc );
    if(!SQL_SUCCEEDED(ret)) {
        printf("Error disconnecting from database. Transaction still open?\n");
        exit(EXIT_FAILURE);
    }

    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    exit(EXIT_SUCCESS);
}

运行以上代码后,将输出以下内容:

Allocated an environment handle.
Set application version to ODBC 3.
Allocated Database handle.
Connecting to database.
Connected to database.
Disconnecting and freeing handles.

有关使用 SQLDriverConnect 连接到数据库的示例,请参阅设置 ODBC 会话的区域设置和编码

注意

  • 如果您使用 DataDirect® 驱动程序管理器,则应在连接到 Vertica 时始终对 SQLDriverConnect 函数的 DriverCompletion 参数(函数调用中的最终参数)使用 SQL_DRIVER_NOPROMPT 值。Linux 和 UNIX 平台上 Vertica 的 ODBC 驱动程序不包含 UI,因此无法提示用户输入密码。

  • 在 Windows 客户端平台上,ODBC 驱动程序会提示用户提供连接信息。有关详细信息,请参阅提示 Windows 用户提供缺少的连接属性

  • 如果数据库不符合 Vertica 许可协议,应用程序将在 SQLConnect() 函数的返回值中收到警告消息。始终应让应用程序检查此返回值是否为 SQL_SUCCESS_WITH_INFO。如果是,应让应用程序提取消息并向用户显示。

2.3.1.6 - 负载均衡

本机连接负载均衡

本机连接负载均衡有助于在 Vertica 数据库中的主机上分散客户端连接所带来的开销。服务器和客户端都必须启用本机连接负载均衡。如果两者者都启用了本机负载均衡,则当客户端最初连接到数据库中的主机时,此主机会从数据库中当前正在运行的主机列表中选择一个主机来处理客户端连接,并通知客户端它所选择的主机。

如果最初联系的主机没有选择自身来处理连接,则客户端会断开连接,然后打开指向第一个主机所选主机的另一个连接。指向此第二个主机的连接进程将照常进行—如果启用了 SSL,则会启动 SSL 协商,否则客户端会启动身份验证过程。有关详细信息,请参阅关于本机连接负载均衡

若要在客户端上启用本机连接负载均衡,请在 DSN 条目或连接字符串中将 ConnectionLoadBalance 连接参数设置为 true。以下示例说明了如何在本机连接负载均衡已启用的情况下多次连接到数据库,以及从 V_MONITOR.CURRENT_SESSION 系统表获取处理连接的节点的名称。

// Demonstrate enabling native load connection balancing.
// Standard i/o library
#include <stdlib.h>
#include <iostream>
#include <assert.h>
// Only needed for Windows clients
// #include <windows.h>
// SQL include files that define data types and ODBC API
// functions
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>

using namespace std;
int main()
{
    SQLRETURN ret;   // Stores return value from ODBC API calls
    SQLHENV hdlEnv;  // Handle for the SQL environment object
    // Allocate an a SQL environment object
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    assert(SQL_SUCCEEDED(ret));

    // Set the ODBC version we are going to use to
    // 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
            (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    assert(SQL_SUCCEEDED(ret));

    // Allocate a database handle.
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    assert(SQL_SUCCEEDED(ret));

    // Connect four times. If load balancing is on, client should
    // connect to different nodes.
    for (int x=1; x <= 4; x++) {

        // Connect to the database using SQLDriverConnect. Set
        // ConnectionLoadBalance to 1 (true) to enable load
        // balancing.
        cout << endl << "Connection attempt #" << x << "... ";
        const char *connStr = "DSN=VMart;ConnectionLoadBalance=1;"
            "UID=ExampleUser;PWD=password123";


        ret = SQLDriverConnect(hdlDbc, NULL, (SQLCHAR*)connStr, SQL_NTS,
               NULL, 0, NULL, SQL_DRIVER_NOPROMPT );
        if(!SQL_SUCCEEDED(ret)) {
            cout << "failed. Exiting." << endl;
            exit(EXIT_FAILURE);
        } else {
            cout << "succeeded" << endl;
        }
        // We're connected. Query the v_monitor.current_session table to
        // find the name of the node we've connected to.

        // Set up a statement handle
        SQLHSTMT hdlStmt;
        SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);
        assert(SQL_SUCCEEDED(ret));

        ret = SQLExecDirect( hdlStmt, (SQLCHAR*)"SELECT node_name FROM "
            "V_MONITOR.CURRENT_SESSION;", SQL_NTS );

        if(SQL_SUCCEEDED(ret)) {
            // Bind varible to column in result set.
            SQLTCHAR node_name[256];
            ret = SQLBindCol(hdlStmt, 1, SQL_C_TCHAR, (SQLPOINTER)node_name,
                sizeof(node_name), NULL);
            while(SQL_SUCCEEDED(ret = SQLFetchScroll(hdlStmt, SQL_FETCH_NEXT,1))) {
                // Print the bound variables, which now contain the values from the
                // fetched row.
                cout << "Connected to node " << node_name << endl;
            }
        }
        // Free statement handle
        SQLFreeHandle(SQL_HANDLE_STMT,hdlStmt);
        cout << "Disconnecting." << endl;
        ret = SQLDisconnect( hdlDbc );
        assert(SQL_SUCCEEDED(ret));
    }
    // When done, free all of the handles to close them
    // in an orderly fashion.
    cout << endl << "Freeing handles..." << endl;
    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    cout << "Done!" << endl;
    exit(EXIT_SUCCESS);
}

运行以上示例后,将生成类似于以下内容的输出:

Connection attempt #1... succeeded
Connected to node v_vmart_node0001
Disconnecting.

Connection attempt #2... succeeded
Connected to node v_vmart_node0002
Disconnecting.

Connection attempt #3... succeeded
Connected to node v_vmart_node0003
Disconnecting.

Connection attempt #4... succeeded
Connected to node v_vmart_node0001
Disconnecting.

Freeing handles...
Done!

基于主机名的负载均衡

您还可以通过将单个主机名解析为多个 IP 地址来平衡工作负载。ODBC 客户端驱动程序通过自动将主机名随机解析为指定的 IP 地址之一来实现负载均衡。

例如,假设主机名 verticahost.example.cometc/hosts 中有以下条目:

192.0.2.0 verticahost.example.com
192.0.2.1 verticahost.example.com
192.0.2.2 verticahost.example.com

指定主机名 verticahost.example.com 会随机解析为列出的 IP 地址之一。

2.3.1.7 - 连接故障转移

如果客户端应用程序尝试连接到 Vertica 群集中处于关闭状态的主机,则使用默认连接配置时连接尝试将会失败。此故障通常会向用户返回一个错误。用户必须等待主机恢复并重试连接,或者手动编辑连接设置以选择其他主机。

由于 Vertica 分析型数据库采用分布式架构,通常您不会关注哪个数据库主机处理客户端应用程序的连接。可以使用客户端驱动程序的连接故障转移功能来防止用户在无法访问连接设置中所指定主机的情况下收到连接错误。JDBC 驱动程序可通过几种方式让客户端驱动程序在无法访问连接参数中所指定的主机时自动尝试连接到其他主机:

  • 将 DNS 服务器配置为返回一个主机名的多个 IP 地址。在连接设置中使用此主机名时,客户端会尝试连接到 DNS 找到的第一个 IP 地址。如果位于该 IP 地址的主机无法访问,则客户端会尝试连接到第二个 IP,依此类推,直到其成功地连接到某个主机或试过所有 IP 地址为止。

  • 提供当您在连接参数中指定的主要主机无法访问时客户端驱动程序尝试连接的备份主机列表。

  • (仅限 JDBC)在尝试连接到下一个节点之前,使用特定于驱动程序的连接属性来管理超时。

在所有方法中,故障转移过程对于客户端应用程序是透明的(除了在选择使用列表故障转移方法时需指定备份主机列表之外)。如果主要主机无法访问,客户端驱动程序会自动尝试连接到其他主机。

故障转移仅适用于初次建立客户端连接的情况。如果连接断开,驱动程序不会自动尝试重新连接到数据库中的其他主机。

通常可选择上述两种故障转移方法中的一种。但它们可以一起使用。如果您的 DNS 服务器返回多个 IP 地址,并且您提供了备份主机列表,则客户端会首先尝试连接 DNS 服务器返回的所有 IP,然后再尝试连接备份列表中的主机。

DNS 故障转移方法可集中处理配置客户端故障转移。在向 Vertica 分析型数据库群集添加新节点时,可以选择通过编辑 DNS 服务器设置来将这些节点添加到故障转移列表中。所有使用 DNS 服务器连接到 Vertica 分析型数据库的客户端系统都会自动采用连接故障转移,而无需更改任何设置。但此方法需要对所有客户端用于连接到 Vertica 分析型数据库群集的 DNS 服务器具有管理访问权限。这在贵组织中可能无法实现。

使用备份服务器列表要比编辑 DNS 服务器设置更加容易。但此方法不能集中处理故障转移功能。如果更改了 Vertica 分析型数据库群集,您可能需要在每个客户端系统上更新应用程序设置。

使用 DNS 故障转移

若要使用 DNS 故障转移,您需要更改 DNS 服务器的设置,将单一主机名映射为 Vertica 分析型数据库群集中主机的多个 IP 地址。然后让所有客户端应用程序都使用该主机名连接到 Vertica 分析型数据库。

您可以选择让 DNS 服务器为主机名返回任何所需数量的 IP 地址。在小型群集中,您可以选择让其返回群集中所有主机的 IP 地址。但对于大型群集,应考虑选择返回一部分主机。否则可能会导致长时间延迟,因为客户端驱动程序尝试连接到数据库中处于关闭状态的每个主机会失败。

使用备份主机列表

若要启用基于备份列表的连接故障转移,需要在客户端应用程序的 BackupServerNode 参数中指定主机的至少一个 IP 地址或主机名。可以视情况在主机名或 IP 后面使用冒号和端口号。如果未提供端口号,驱动程序会默认使用标准 Vertica 端口号 (5433)。若要列出多个主机,请用逗号分隔这些主机。

以下示例演示了如何通过设置 BackupServerNode 连接参数来指定可尝试连接的其他主机。由于有意在连接字符串中使用了一个不存在的节点,因此初始连接失败。客户端驱动程序必须尝试通过备份主机来与 Vertica 建立连接。

// Demonstrate using connection failover.
// Standard i/o library
#include <stdlib.h>
#include <iostream>
#include <assert.h>

// Only needed for Windows clients
// #include <windows.hgt;

// SQL include files that define data types and ODBC API
// functions
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>

using namespace std;

int main()
{
    SQLRETURN ret;   // Stores return value from ODBC API calls
    SQLHENV hdlEnv;  // Handle for the SQL environment object
    // Allocate an a SQL environment object
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    assert(SQL_SUCCEEDED(ret));

    // Set the ODBC version we are going to use to
    // 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
            (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    assert(SQL_SUCCEEDED(ret));

    // Allocate a database handle.
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    assert(SQL_SUCCEEDED(ret));

/* DSN for this connection specifies a bad node, and good backup nodes:
[VMartBadNode]
Description=VMart Vertica Database
Driver=/opt/vertica/lib64/libverticaodbc.so
Database=VMart
Servername=badnode.example.com
BackupServerNode=v_vmart_node0002.example.com,v_vmart_node0003.example.com
*/

    // Connect to the database using SQLConnect
    cout << "Connecting to database." << endl;
    const char *dsnName = "VMartBadNode"; // Name of the DSN
    const char* userID = "ExampleUser"; // Username
    const char* passwd = "password123"; // password
    ret = SQLConnect(hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS,(SQLCHAR*)userID,SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        cout << "Could not connect to database." << endl;
        exit(EXIT_FAILURE);
    } else {
        cout << "Connected to database." << endl;
    }
    // We're connected. Query the v_monitor.current_session table to
    // find the name of the node we've connected to.

    // Set up a statement handle
    SQLHSTMT hdlStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);
    assert(SQL_SUCCEEDED(ret));

    ret = SQLExecDirect( hdlStmt, (SQLCHAR*)"SELECT node_name FROM "
        "v_monitor.current_session;", SQL_NTS );

    if(SQL_SUCCEEDED(ret)) {
        // Bind varible to column in result set.
        SQLTCHAR node_name[256];
        ret = SQLBindCol(hdlStmt, 1, SQL_C_TCHAR, (SQLPOINTER)node_name,
            sizeof(node_name), NULL);
        while(SQL_SUCCEEDED(ret = SQLFetchScroll(hdlStmt, SQL_FETCH_NEXT,1))) {
            // Print the bound variables, which now contain the values from the
            // fetched row.
            cout << "Connected to node " << node_name << endl;
        }
    }

    cout << "Disconnecting." << endl;
    ret = SQLDisconnect( hdlDbc );
    assert(SQL_SUCCEEDED(ret));

    // When done, free all of the handles to close them
    // in an orderly fashion.
    cout << endl << "Freeing handles..." << endl;
    SQLFreeHandle(SQL_HANDLE_STMT,hdlStmt);
    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    cout << "Done!" << endl;
    exit(EXIT_SUCCESS);
}

运行此示例后,系统控制台上的输出类似于以下内容:

Connecting to database.
Connected to database.
Connected to node v_vmart_node0002
Disconnecting.

Freeing handles...
Done!

请注意,系统会与备份列表中的第一个节点建立连接(节点 2)。

2.3.1.8 - 提示 Windows 用户提供缺少的连接属性

如果缺少必需信息,Vertica Windows ODBC 驱动程序会提示用户提供连接信息。当客户端应用程序调用 SQLDriverConnect 以连接到 Vertica 并且以下任一条件成立时,驱动程序将显示“Vertica 连接对话框 (Vertica Connection Dialog)”:

  • DriverCompletion 属性已设置为 SQL_DRIVER_PROMPT。

  • DriverCompletion 属性已设置为 SQL_DRIVER_COMPLETE 或 SQL_DRIVER_COMPLETE_REQUIRED,且用于建立连接的连接字符串或 DSN 缺少服务器、数据库或端口信息。

如果以上任一条件成立,驱动程序将向用户显示“Vertica 连接对话框 (Vertica Connection Dialog)”以提示用户提供连接信息。

该对话框包含填入的连接字符串或 DSN 中提供的所有属性值。

连接对话框上的必填字段是“数据库 (Database)”、“UID”、“服务器 (Server)”和“端口 (Port)”。填充这些字段后,表单将启用确定 (OK) 按钮。

如果用户在该对话框上单击取消 (Cancel)SQLDriverConnect 函数调用将立即返回 SQL_NO_DATA,而不会尝试连接到 Vertica。如果用户提供的连接信息不完整或不正确,连接函数将在连接尝试失败后返回 SQL_ERROR。

2.3.1.9 - 提示 Windows 用户提供密码

如果向 SQLDriverConnect 函数(客户端应用程序调用此函数以连接到 Vertica)提供的连接字符串或 DSN 缺少连接到数据库所需的任何必需的连接属性,Vertica Windows ODBC 驱动程序将打开一个对话框,提示用户输入缺失的信息(请参阅提示 Windows 用户提供缺少的连接属性)。一般情况下,不将用户密码视为必需的连接属性,因为 Vertica 用户帐户可能不具有密码。如果缺少密码属性,ODBC 驱动程序仍会在未提供密码的情况下尝试连接到 Vertica。

您可以使用 PromptOnNoPassword DSN 参数来强制 ODBC 驱动程序将密码视为必需的连接属性。如果不想在 DSN 条目中存储密码,则此参数很有用。保存在 DSN 条目中的密码是不安全的,因为这些密码作为明文存储在 Windows 注册表中,从而对同一个系统上的其他用户可见。

还有另外两个因素决定 ODBC 驱动程序是否显示 Vertica“连接 (Connection)”对话框。这两个因素如下所示(按优先级顺序):

  • SQLDriverConnect 函数调用的 DriverCompletion 参数。

  • DSN 或连接字符串是否包含密码。

下表显示了 PromptOnNoPassword DSN 参数、SQLDriverConnect 函数的 DriverCompletion 参数以及“DSN 或连接字符串是否包含密码”如何交互以控制是否显示 Vertica“连接 (Connection)”对话框。

以下示例代码演示了如何在 C++ 中将 PromptOnNoPassword DSN 参数和系统 DSN 一起使用:

wstring connectString = L "DSN=VerticaDSN;PromptOnNoPassword=1;";
retcode = SQLDriverConnect(
    hdbc,
    0,
    (SQLWCHAR * ) connectString.c_str(),
    connectString.length(),
    OutConnStr,
    255, &
    amp; OutConnStrLen,
    SQL_DRIVER_COMPLETE);

无密码条目与空密码

连接字符串或 DSN 中不包含密码和包含空密码是两种不同情况。仅当连接字符串或 DSN 不具有 PWD 属性(存放用户密码)时,PromptOnNoPassword DSN 参数才起作用。如果具有该属性,则即使该属性为空,PromptOnNoPassword 也不会提示 Windows ODBC 驱动程序显示 Vertica“连接 (Connection)”对话框。

如果要使用 DSN 为连接提供属性,则此差别会造成混乱。在 Windows ODBC 管理器中为 DSN 连接输入密码并保存该 DSN 连接后,Windows 会将 PWD 属性添加到注册表中的 DSN 定义。如果以后删除该密码,PWD 属性会保留在 DSN 定义中(只会将其值设置为空字符串)。即使您只是使用 ODBC 管理器对话框上的“测试 (Test)”按钮测试 DSN,并且稍后在保存之前清除了该 DSN,也会创建 PWD 属性。

设置了密码后,从 DSN 定义中移除 PWD 属性的唯一方法是使用 Windows 注册表编辑器:

  1. 单击 Windows“开始”菜单,然后单击“运行”。

  2. 在“运行”对话框中,键入 regedit,然后单击“确定”。

  3. 在“注册表编辑器 (Registry Editor)”窗口中,单击“编辑 (Edit)”>“查找 (Find)”(或按 Ctrl+F)。

  4. 在“查找”窗口中,输入要删除其 PWD 属性的 DSN 的名称,然后单击“确定”。

  5. 如果查找操作无法定位到 ODBC.INI 文件夹下的某个文件夹,请单击“编辑 (Edit)”>“查找下一个 (Find Next)”(或按 F3),直至已突出显示与 DSN 的名称匹配的文件夹为止。

  6. 选择 PWD 项,然后按 Delete。

  7. 单击“是”以确认删除该值。

该 DSN 现已不具有 PWD 属性,并且可以在与 PromptOnNoPassword=true 和 DriverConnect=SQL_DRIVER_COMPLETE 一起使用时促使连接对话框显示出来。

2.3.1.10 - 设置 ODBC 会话的区域设置和编码

Vertica 提供以下方法来设置 ODBC 会话的区域设置和编码:

  • 使用 DSN 指定所有已建立的连接的区域设置:

  • SQLDriverConnect() 函数的连接字符串中设置 Locale 连接参数。例如:

    SQLDriverConnect(conn, NULL, (SQLCHAR*)"DSN=Vertica;Locale=en_GB@collation=binary", SQL_NTS, szConnOut, sizeof(szConnOut), &iAvailable, SQL_DRIVER_NOPROMPT)
    
  • 使用 SQLSetConnectAttr() 设置编码和区域设置。通常,您应该始终使用此函数设置编码,而不是在 DSN 中设置它。

    • 传递 SQL_ATTR_VERTICA_LOCALE 常量和 ICU 字符串作为属性值。例如:

      => SQLSetConnectAttr(hdlDbc, SQL_ATTR_VERTICA_LOCALE, (SQLCHAR*)newLocale,
              SQL_NTS);
      
    • 传递 SQL_ATTR_AP_WCHAR_TYPE 常量和编码作为属性值。例如:

      => rc = SQLSetConnectAttr (hdbc, SQL_ATTR_APP_WCHAR_TYPE, (void *)SQL_DD_CP_UTF16, SQL_IS_INTEGER);
      

注意

  • 如果让客户端系统使用非 Unicode 区域设置(例如,在 Linux 平台上设置 LANG=C)并对与 Vertica 的连接使用 Unicode 区域设置,则会生成诸如“(10170) 来自数据源的数据进行字符串数据右截断 (String data right truncation on data from data source)”等错误。如果从 Vertica 收到的数据不采用 UTF-8 格式,驱动程序将基于系统的区域设置来分配字符串内存,并且非 UTF-8 数据会触发超限。如果始终在客户端系统上使用 Unicode 区域设置,您可以避免这些错误。

    如果在连接字符串或 DSN 中指定区域设置,调用连接函数将返回有关成功连接的 SQL_SUCCESS_WITH_INFO,并显示有关区域设置状态的消息。

  • ODBC 应用程序可以处于 ANSI 模式或 Unicode 模式:

    • 如果处于 Unicode 模式,则 ODBC 使用的编码是 UCS-2。

    • 如果处于 ANSI 模式,则数据必须采用单字节 ASCII 编码,此编码与数据库服务器上的 UTF-8 兼容。

    向 Vertica 服务器传递数据时,ODBC 驱动程序会将 UCS-2 转换为 UTF-8,并会将 Vertica 服务器发来的数据从 UTF-8 转换为 UCS-2。

  • 如果最终用户应用程序尚未处于 UCS-2 编码模式,则应用程序应负责将输入数据转换为 UCS-2,否则会出现意外结果。例如:

    • 向 ODBC API 传递非 UCS-2 数据时,如果该数据解释为 UCS-2,将导致向 API 传递无效的 UCS-2 符号,从而生成错误。

    • 或者在备用编码中提供的符号可能是有效的 UCS-2 符号;在这种情况下,会将不正确的数据插入到数据库。

    ODBC 应用程序应使用 SQLSetConnectAttr 设置正确的服务器会话区域设置(如果与数据库范围设置不同),以便在服务器上设置正确的排序规则和字符串函数行为。

以下示例代码演示了同时使用连接字符串和 SQLSetConnectAttr() 函数来设置区域设置。

// Standard i/o library
#include <stdio.h>
#include <stdlib.h>
// Only needed for Windows clients
// #include <windows.h>
// SQL include files that define data types and ODBC API
// functions
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
// Vertica-specific definitions. This include file is located as
// /opt/vertica/include on database hosts.
#include <verticaodbc.h>
int main()
{
    SQLRETURN ret;   // Stores return value from ODBC API calls
    SQLHENV hdlEnv;  // Handle for the SQL environment object
    // Allocate an a SQL environment object
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate a handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated an environment handle.\n");
    }
    // Set the ODBC version we are going to use to 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
        (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not set application version to ODBC 3.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Set application version to ODBC 3.\n");
    }
    // Allocate a database handle.
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate database handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated Database handle.\n");
    }
    // Connect to the database using SQLDriverConnect
    printf("Connecting to database.\n");
    // Set the locale to English in Great Britain.
    const char *connStr = "DSN=ExampleDB;locale=en_GB;"
        "UID=dbadmin;PWD=password123";
    ret = SQLDriverConnect(hdlDbc, NULL, (SQLCHAR*)connStr, SQL_NTS,
               NULL, 0, NULL, SQL_DRIVER_NOPROMPT );
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not connect to database.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Connected to database.\n");
    }
    // Get the Locale
    char locale[256];
    SQLGetConnectAttr(hdlDbc, SQL_ATTR_VERTICA_LOCALE, locale, sizeof(locale),
        0);
    printf("Locale is set to: %s\n", locale);
    // Set the locale to a new value
    const char* newLocale = "en_GB";
    SQLSetConnectAttr(hdlDbc, SQL_ATTR_VERTICA_LOCALE, (SQLCHAR*)newLocale,
        SQL_NTS);

    // Get the Locale again
    SQLGetConnectAttr(hdlDbc, SQL_ATTR_VERTICA_LOCALE, locale, sizeof(locale),
        0);
    printf("Locale is now set to: %s\n", locale);

    // Set the encoding
    SQLSetConnectAttr (hdbc, SQL_ATTR_APP_WCHAR_TYPE, (void *)SQL_DD_CP_UTF16,
        SQL_IS_INTEGER);

    // When done, free all of the handles to close them
    // in an orderly fashion.
    printf("Disconnecting and freeing handles.\n");
    ret = SQLDisconnect( hdlDbc );
    if(!SQL_SUCCEEDED(ret)) {
        printf("Error disconnecting from database. Transaction still open?\n");
        exit(EXIT_FAILURE);
    }
    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    exit(EXIT_SUCCESS);
}

2.3.1.11 - AUTOCOMMIT 事务和 ODBC 事务

AUTOCOMMIT 连接属性控制 INSERT、ALTER、COPY 和其他数据处理语句是否在完成后自动提交。默认情况下,AUTOCOMMIT 已启用,所有语句将在执行后提交。这通常不是最佳设置,因为效率较低。此外,您通常想要控制是否作为一个整体提交一组语句,而非逐个提交语句。例如,您可能只想在所有插入都成功时才提交一系列插入。如果 AUTOCOMMIT 已禁用,则当其中一个语句失败时,您可以回退事务。

如果 AUTOCOMMIT 已打开,则会在执行语句之后立即提交这些语句的结果。不能回退在 AUTOCOMMIT 模式下执行的语句。

例如,如果 AUTOCOMMIT 已打开,将自动提交以下单个 INSERT 语句:

ret = SQLExecDirect(hdlStmt, (SQLCHAR*)"INSERT INTO customers VALUES(500,"
    "'Smith, Sam', '123-456-789');", SQL_NTS);

如果 AUTOCOMMIT 已关闭,您需要在执行语句之后手动提交事务。例如:

ret = SQLExecDirect(hdlStmt, (SQLCHAR*)"INSERT INTO customers VALUES(500,"
    "'Smith, Sam', '123-456-789');", SQL_NTS);
// Other inserts and data manipulations
// Commit the statements(s)
ret = SQLEndTran(SQL_HANDLE_DBC, hdlDbc, SQL_COMMIT);

只有在调用 SQLEndTran() 时,才会提交插入的行。提交事务之前,您可以随时回退 INSERT 和其他语句。

以下示例演示了关闭 AUTOCOMMIT 以及执行插入和手动提交事务。

// Some standard headers
#include <stdio.h>
#include <stdlib.h>
// Only needed for Windows clients
// #include <windows.h>
// Standard ODBC headers
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
int main()
{
    // Set up the ODBC environment
    SQLRETURN ret;
    SQLHENV hdlEnv;
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate a handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated an environment handle.\n");
    }
    // Tell ODBC that the application uses ODBC 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
        (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not set application version to ODBC3.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Set application to ODBC 3.\n");
    }
    // Allocate a database handle.
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate database handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated Database handle.\n");
    }
    // Connect to the database
    printf("Connecting to database.\n");
    const char *dsnName = "ExampleDB";
    const char* userID = "dbadmin";
    const char* passwd = "password123";
    ret = SQLConnect(hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS,(SQLCHAR*)userID,SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not connect to database.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Connected to database.\n");
    }
    // Get the AUTOCOMMIT state
    SQLINTEGER  autoCommitState;
    SQLGetConnectAttr(hdlDbc, SQL_ATTR_AUTOCOMMIT, &autoCommitState, 0, NULL);
    printf("Autocommit is set to: %d\n", autoCommitState);


    // Disable AUTOCOMMIT
    printf("Disabling autocommit.\n");
    ret = SQLSetConnectAttr(hdlDbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF,
        SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not disable autocommit.\n");
        exit(EXIT_FAILURE);
    }

    // Get the AUTOCOMMIT state again
    SQLGetConnectAttr(hdlDbc, SQL_ATTR_AUTOCOMMIT, &autoCommitState, 0, NULL);
    printf("Autocommit is set to: %d\n", autoCommitState);

    // Set up a statement handle
    SQLHSTMT hdlStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);


    // Create a table to hold the data
    SQLExecDirect(hdlStmt, (SQLCHAR*)"DROP TABLE IF EXISTS customers",
        SQL_NTS);
    SQLExecDirect(hdlStmt, (SQLCHAR*)"CREATE TABLE customers "
        "(CustID int, CustName varchar(100), Phone_Number char(15));",
        SQL_NTS);


    // Insert a single row.
    ret = SQLExecDirect(hdlStmt, (SQLCHAR*)"INSERT INTO customers VALUES(500,"
        "'Smith, Sam', '123-456-789');", SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not perform single insert.\n");
    } else {
        printf("Performed single insert.\n");
    }


    // Need to commit the transaction before closing, since autocommit is
    // disabled. Otherwise SQLDisconnect returns an error.
    printf("Committing transaction.\n");
    ret =  SQLEndTran(SQL_HANDLE_DBC, hdlDbc, SQL_COMMIT);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Error committing transaction.\n");
        exit(EXIT_FAILURE);
    }

    // Clean up
    printf("Free handles.\n");
    ret = SQLDisconnect(hdlDbc);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Error disconnecting from database. Transaction still open?\n");
        exit(EXIT_FAILURE);
    }
    SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    exit(EXIT_SUCCESS);
}

运行以上代码后,将生成以下输出:

Allocated an environment handle.
Set application to ODBC 3.
Allocated Database handle.
Connecting to database.
Connected to database.
Autocommit is set to: 1
Disabling autocommit.
Autocommit is set to: 0
Performed single insert.
Committing transaction.
Free handles.

2.3.1.12 - 检索数据

若要通过 ODBC 检索数据,应执行将返回结果集的查询(例如 SELECT),然后使用以下两种方法之一检索结果:

  • 使用 SQLFetch() 函数检索结果集的某个行,然后通过调用 SQLGetData() 访问该行中的列值。

  • 使用 SQLBindColumn() 函数将变量或数组绑定到结果集中的某个列,然后调用 SQLExtendedFetch()SQLFetchScroll() 以读取结果集的某个行并将其值插入到该变量或数组中。

使用以上两种方法时,应在结果集中循环直至到达结尾(由 SQL_NO_DATA 返回状态指示)或遇到错误为止。

以下代码示例演示了如何通过以下过程从 Vertica 检索数据:

  1. 连接到数据库。

  2. 执行将返回所有表的 ID 和名称的 SELECT 语句。

  3. 将两个变量绑定到结果集中的两个列。

  4. 在结果集中循环,并输出 ID 和名称值。

// Demonstrate running a query and getting results by querying the tables
// system table for a list of all tables in the current schema.
// Some standard headers
#include <stdlib.h>
#include <sstream>
#include <iostream>
#include <assert.h>
// Standard ODBC headers
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>

// Use std namespace to make output easier
using namespace std;
// Helper function to print SQL error messages.
template <typename HandleT>
void reportError(int handleTypeEnum, HandleT hdl)
{
    // Get the status records.
    SQLSMALLINT   i, MsgLen;
    SQLRETURN ret2;
    SQLCHAR       SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH];
    SQLINTEGER    NativeError;
    i = 1;
    cout << endl;
    while ((ret2 = SQLGetDiagRec(handleTypeEnum, hdl, i, SqlState, &NativeError,
        Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) {
        cout << "error record #" << i++ << endl;
        cout << "sqlstate: " << SqlState << endl;
        cout << "detailed msg: " << Msg << endl;
        cout << "native error code: " << NativeError << endl;
    }
}

typedef struct {
    SQLHENV hdlEnv;
    SQLHDBC hdlDbc;
} DBConnection;

void connect(DBConnection *pConnInfo)
{
    // Set up the ODBC environment
    SQLRETURN ret;
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &pConnInfo->hdlEnv);
    assert(SQL_SUCCEEDED(ret));
    // Tell ODBC that the application uses ODBC 3.
    ret = SQLSetEnvAttr(pConnInfo->hdlEnv, SQL_ATTR_ODBC_VERSION,
        (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_UINTEGER);
    assert(SQL_SUCCEEDED(ret));

    // Allocate a database handle.
    ret = SQLAllocHandle(SQL_HANDLE_DBC, pConnInfo->hdlEnv, &pConnInfo->hdlDbc);
    assert(SQL_SUCCEEDED(ret));
    // Connect to the database
    cout << "Connecting to database." << endl;
    const char* dsnName = "ExampleDB";
    const char* userID = "dbadmin";
    const char* passwd = "password123";
    ret = SQLConnect(pConnInfo->hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS, (SQLCHAR*)userID, SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if (!SQL_SUCCEEDED(ret)) {
        cout << "Could not connect to database" << endl;
        reportError<SQLHDBC>(SQL_HANDLE_DBC, pConnInfo->hdlDbc);
        exit(EXIT_FAILURE);
    }
    else {
        cout << "Connected to database." << endl;
    }
}

void disconnect(DBConnection *pConnInfo)
{
    SQLRETURN ret;
    // Clean up by shutting down the connection
    cout << "Free handles." << endl;
    ret = SQLDisconnect(pConnInfo->hdlDbc);
    if (!SQL_SUCCEEDED(ret)) {
        cout << "Error disconnecting. Transaction still open?" << endl;
        exit(EXIT_FAILURE);
    }
    SQLFreeHandle(SQL_HANDLE_DBC, pConnInfo->hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, pConnInfo->hdlEnv);
}

void executeQuery(SQLHDBC hdlDbc, SQLCHAR* pQuery)
{
    SQLRETURN ret;
    // Set up a statement handle
    SQLHSTMT hdlStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);
    assert(SQL_SUCCEEDED(ret));

    // Execute a query to get the names and IDs of all tables in the schema
    // search p[ath (usually public).
    ret = SQLExecDirect(hdlStmt, pQuery, SQL_NTS);

    if (!SQL_SUCCEEDED(ret)) {
        // Report error an go no further if statement failed.
        cout << "Error executing statement." << endl;
        reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);
        exit(EXIT_FAILURE);
    }
    else {
        // Query succeeded, so bind two variables to the two colums in the
        // result set,
        cout << "Fetching results..." << endl;
        SQLBIGINT table_id;       // Holds the ID of the table.
        SQLTCHAR table_name[256]; // buffer to hold name of table
        ret = SQLBindCol(hdlStmt, 1, SQL_C_SBIGINT, (SQLPOINTER)&table_id,
            sizeof(table_id), NULL);
        ret = SQLBindCol(hdlStmt, 2, SQL_C_TCHAR, (SQLPOINTER)table_name,
            sizeof(table_name), NULL);

        // Loop through the results,
        while (SQL_SUCCEEDED(ret = SQLFetchScroll(hdlStmt, SQL_FETCH_NEXT, 1))) {
            // Print the bound variables, which now contain the values from the
            // fetched row.
            cout << table_id << " | " << table_name << endl;
        }


        // See if loop exited for reasons other than running out of data
        if (ret != SQL_NO_DATA) {
            // Exited for a reason other than no more data... report the error.
            reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);
        }
    }
    SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
}

int main()
{
    DBConnection conn;

    connect(&conn);
    executeQuery(conn.hdlDbc,
        (SQLCHAR*)"SELECT table_id, table_name FROM tables ORDER BY table_name");
    executeQuery(conn.hdlDbc,
        (SQLCHAR*)"SELECT table_id, table_name FROM tables ORDER BY table_id");
    disconnect(&conn);
    exit(EXIT_SUCCESS);
}

在 vmart 数据库中运行以上示例代码后,将生成类似于以下内容的输出:

Connecting to database.
Connected to database.
Fetching results...
45035996273970908 | call_center_dimension
45035996273970836 | customer_dimension
45035996273972958 | customers
45035996273970848 | date_dimension
45035996273970856 | employee_dimension
45035996273970868 | inventory_fact
45035996273970904 | online_page_dimension
45035996273970912 | online_sales_fact
45035996273970840 | product_dimension
45035996273970844 | promotion_dimension
45035996273970860 | shipping_dimension
45035996273970876 | store_dimension
45035996273970894 | store_orders_fact
45035996273970880 | store_sales_fact
45035996273972806 | t
45035996273970852 | vendor_dimension
45035996273970864 | warehouse_dimension
Fetching results...
45035996273970836 | customer_dimension
45035996273970840 | product_dimension
45035996273970844 | promotion_dimension
45035996273970848 | date_dimension
45035996273970852 | vendor_dimension
45035996273970856 | employee_dimension
45035996273970860 | shipping_dimension
45035996273970864 | warehouse_dimension
45035996273970868 | inventory_fact
45035996273970876 | store_dimension
45035996273970880 | store_sales_fact
45035996273970894 | store_orders_fact
45035996273970904 | online_page_dimension
45035996273970908 | call_center_dimension
45035996273970912 | online_sales_fact
45035996273972806 | t
45035996273972958 | customers
Free handles.

2.3.1.13 - 加载数据

许多客户端应用程序的主要任务是将数据加载到 Vertica 数据库。有多种不同方法可用于通过 ODBC 插入数据,本节中的主题介绍这些方法。

2.3.1.13.1 - 使用单行插入

将数据加载到 Vertica 的最简单方法是使用 SQLExecuteDirect 函数运行 INSERT SQL 语句。但此方法仅能插入单个数据行。

ret = SQLExecDirect(hstmt, (SQLTCHAR*)"INSERT into Customers values"
      "(1,'abcda','efgh','1')", SQL_NTS);

2.3.1.13.2 - 使用预定义的语句

Vertica 支持将服务器端预定义的语句与 ODBC 和 JDBC 结合使用。使用预定义的语句,您只需定义一个语句一次,然后可以使用不同的参数多次运行该语句。要执行的语句包含占位符而非参数。执行语句时,应为每个占位符提供值。

在以下示例查询中,占位符用问号 (?) 表示:

SELECT * FROM public.inventory_fact WHERE product_key = ?

服务器端预定义的语句用于以下用途:

  • 优化查询。Vertica 只需要解析语句一次。

  • 防止 SQL 注入攻击。如果过滤用户输入时未能正确过滤嵌入在 SQL 语句中的字符串字面量转义字符,或者如果用户输入由于未强类型化而意外运行,则将发生 SQL 注入攻击。由于预定义的语句与输入数据分开进行解析,因此数据库不可能意外执行数据。

  • 将直接变量绑定到返回列。通过指向数据结构,代码不必执行额外转换。

以下示例演示了使用预定义的语句执行单一插入。

// Some standard headers
#include <stdio.h>
#include <stdlib.h>
// Only needed for Windows clients
// #include <windows.h>
// Standard ODBC headers
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
// Some constants for the size of the data to be inserted.
#define CUST_NAME_LEN 50
#define PHONE_NUM_LEN 15
#define NUM_ENTRIES 4
int main()
{
    // Set up the ODBC environment
    SQLRETURN ret;
    SQLHENV hdlEnv;
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate a handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated an environment handle.\n");
    }
    // Tell ODBC that the application uses ODBC 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
        (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not set application version to ODBC3.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Set application to ODBC 3.\n");
    }
    // Allocate a database handle.
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    // Connect to the database
    printf("Connecting to database.\n");
    const char *dsnName = "ExampleDB";
    const char* userID = "dbadmin";
    const char* passwd = "password123";
    ret = SQLConnect(hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS,(SQLCHAR*)userID,SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not connect to database.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Connected to database.\n");
    }

    // Disable AUTOCOMMIT
    printf("Disabling autocommit.\n");
    ret = SQLSetConnectAttr(hdlDbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF,
        SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not disable autocommit.\n");
        exit(EXIT_FAILURE);
    }


    // Set up a statement handle
    SQLHSTMT hdlStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);
    SQLExecDirect(hdlStmt, (SQLCHAR*)"DROP TABLE IF EXISTS customers",
        SQL_NTS);
    SQLExecDirect(hdlStmt, (SQLCHAR*)"CREATE TABLE customers "
        "(CustID int, CustName varchar(100), Phone_Number char(15));",
        SQL_NTS);

    // Set up a bunch of variables to be bound to the statement
    // parameters.

    // Create the prepared statement. This will insert data into the
    // table we created above.
    printf("Creating prepared statement\n");
    ret = SQLPrepare (hdlStmt, (SQLTCHAR*)"INSERT INTO customers (CustID, "
        "CustName,  Phone_Number) VALUES(?,?,?)", SQL_NTS) ;
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not create prepared statement\n");
        SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
        SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
        SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
        exit(EXIT_FAILURE);
    } else {
        printf("Created prepared statement.\n");
    }
    SQLINTEGER custID = 1234;
    SQLCHAR custName[100] = "Fein, Fredrick";
    SQLVARCHAR phoneNum[15] = "555-123-6789";
    SQLLEN strFieldLen = SQL_NTS;
    SQLLEN custIDLen = 0;
    // Bind the data arrays to the parameters in the prepared SQL
    // statement
    ret = SQLBindParameter(hdlStmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
        0, 0, &custID, 0 , &custIDLen);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not bind custID array\n");
        SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
        SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
        SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
        exit(EXIT_FAILURE);
    } else {
        printf("Bound custID to prepared statement\n");
    }
    // Bind CustNames
    SQLBindParameter(hdlStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR,
        50, 0, (SQLPOINTER)custName,  0, &strFieldLen);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not bind custNames\n");
        SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
        SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
        SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
        exit(EXIT_FAILURE);
    } else {
        printf("Bound custName to prepared statement\n");
    }
    // Bind phoneNums
    SQLBindParameter(hdlStmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
        15, 0, (SQLPOINTER)phoneNum, 0, &strFieldLen);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not bind phoneNums\n");
        SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
        SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
        SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
        exit(EXIT_FAILURE);
    } else {
        printf("Bound phoneNum to prepared statement\n");
    }
    // Execute the prepared statement.
    printf("Running prepared statement...");
    ret = SQLExecute(hdlStmt);
    if(!SQL_SUCCEEDED(ret)) {
        printf("not successful!\n");
    }  else {
        printf("successful.\n");
    }

    // Done with batches, commit the transaction
    printf("Committing transaction\n");
    ret = SQLEndTran(SQL_HANDLE_DBC, hdlDbc, SQL_COMMIT);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not commit transaction\n");
    }  else {
        printf("Committed transaction\n");
    }

    // Clean up
    printf("Free handles.\n");
    ret = SQLDisconnect( hdlDbc );
    if(!SQL_SUCCEEDED(ret)) {
        printf("Error disconnecting. Transaction still open?\n");
        exit(EXIT_FAILURE);
    }
    SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    exit(EXIT_SUCCESS);
}

2.3.1.13.3 - 使用批量插入

可以使用批量插入将数据区块插入到数据库中。通过将数据拆分为多个批,您可以在加载每个批后收到有关任何拒绝的行的信息,从而监控加载进度。若要通过 ODBC 执行批量加载,通常可以将预定义的语句与绑定到数组(包含要加载的数据)结合使用。对于每个批,应将新的数据集加载到数组中,然后执行预定义的语句。

执行批量加载时,Vertica 使用 COPY 语句加载数据。所加载的其他每个批使用同一个 COPY 语句。该语句将保持打开,直至您执行以下操作为止:结束语句,关闭语句的光标或执行非 INSERT 语句。

将单个 COPY 语句用于多个批可以通过以下途径提高批量加载效率:

  • 减少插入各个批的开销

  • 将各个批合并为较大的 ROS 容器

虽然 Vertica 使用单个 COPY 语句插入一个事务中的多个批,但您可以在加载每个批之后查找由于行格式无效或存在数据类型问题而被拒绝的行(如果有)。有关详细信息,请参阅跟踪加载状态 (ODBC)

由于批量加载共享一个 COPY 语句,因此一个批中的错误会导致回退同一个事务中较早的批。

批量插入步骤

应用程序执行 ODBC 批量插入所需完成的步骤如下:

  1. 连接到数据库。

  2. 禁用连接的自动提交。

  3. 创建可插入要加载的数据的预定义的语句。

  4. 将该预定义的语句的参数绑定到包含要加载的数据的数组。

  5. 使用各个批的数据填充数组。

  6. 执行该预定义的语句。

  7. (可选)检查批量加载的结果,以查找拒绝的行。

  8. 重复以上三个步骤,直至已加载所有要加载的数据为止。

  9. 提交事务。

  10. (可选)检查整个批处理事务的结果。

以下示例代码演示了以上步骤的简化版本。

// Some standard headers
#include <stdio.h>
#include <stdlib.h>
// Only needed for Windows clients
// #include <windows.h>
// Standard ODBC headers
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
int main()
{
    // Number of data rows to insert
    const int NUM_ENTRIES = 4;

    // Set up the ODBC environment
    SQLRETURN ret;
    SQLHENV hdlEnv;
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate a handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated an environment handle.\n");
    }
    // Tell ODBC that the application uses ODBC 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
        (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not set application version to ODBC3.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Set application to ODBC 3.\n");
    }
    // Allocate a database handle.
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate database handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated Database handle.\n");
    }
    // Connect to the database
    printf("Connecting to database.\n");
    const char *dsnName = "ExampleDB";
    const char* userID = "dbadmin";
    const char* passwd = "password123";
    ret = SQLConnect(hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS,(SQLCHAR*)userID,SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not connect to database.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Connected to database.\n");
    }


    // Disable AUTOCOMMIT
    printf("Disabling autocommit.\n");
    ret = SQLSetConnectAttr(hdlDbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF,
                            SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not disable autocommit.\n");
        exit(EXIT_FAILURE);
    }

    // Set up a statement handle
    SQLHSTMT hdlStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);

    // Create a table to hold the data
    SQLExecDirect(hdlStmt, (SQLCHAR*)"DROP TABLE IF EXISTS customers",
        SQL_NTS);
    SQLExecDirect(hdlStmt, (SQLCHAR*)"CREATE TABLE customers "
        "(CustID int, CustName varchar(100), Phone_Number char(15));",
        SQL_NTS);

    // Create the prepared statement. This will insert data into the
    // table we created above.
    printf("Creating prepared statement\n");
    ret = SQLPrepare (hdlStmt, (SQLTCHAR*)"INSERT INTO customers (CustID, "
        "CustName,  Phone_Number) VALUES(?,?,?)", SQL_NTS) ;
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not create prepared statement\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Created prepared statement.\n");
    }
    // This is the data to be inserted into the database.
    SQLCHAR custNames[][50] = { "Allen, Anna", "Brown, Bill", "Chu, Cindy",
        "Dodd, Don" };
    SQLINTEGER custIDs[] = { 100, 101, 102, 103};
    SQLCHAR phoneNums[][15] = {"1-617-555-1234", "1-781-555-1212",
        "1-508-555-4321", "1-617-555-4444"};
    // Bind the data arrays to the parameters in the prepared SQL
    // statement. First is the custID.
    ret = SQLBindParameter(hdlStmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
        0, 0, (SQLPOINTER)custIDs, sizeof(SQLINTEGER) , NULL);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not bind custID array\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Bound CustIDs array to prepared statement\n");
    }
    // Bind CustNames
    ret = SQLBindParameter(hdlStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR,
        50, 0, (SQLPOINTER)custNames, 50, NULL);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not bind custNames\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Bound CustNames array to prepared statement\n");
    }
    // Bind phoneNums
    ret = SQLBindParameter(hdlStmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
        15, 0, (SQLPOINTER)phoneNums, 15, NULL);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not bind phoneNums\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Bound phoneNums array to prepared statement\n");
    }
    // Tell the ODBC driver how many rows we have in the
    // array.
    ret = SQLSetStmtAttr( hdlStmt, SQL_ATTR_PARAMSET_SIZE,
        (SQLPOINTER)NUM_ENTRIES, 0 );
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not bind set parameter size\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Bound phoneNums array to prepared statement\n");
    }

    // Add multiple batches to the database. This just adds the same
    // batch of data four times for simplicity's sake. Each call adds
    // the 4 rows into the database.
    for (int batchLoop=1; batchLoop<=5; batchLoop++) {
        // Execute the prepared statement, loading all of the data
        // in the arrays.
        printf("Adding Batch #%d...", batchLoop);
        ret = SQLExecute(hdlStmt);
        if(!SQL_SUCCEEDED(ret)) {
           printf("not successful!\n");
        }  else {
            printf("successful.\n");
        }
    }
    // Done with batches, commit the transaction
    printf("Committing transaction\n");
    ret = SQLEndTran(SQL_HANDLE_DBC, hdlDbc, SQL_COMMIT);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not commit transaction\n");
    }  else {
        printf("Committed transaction\n");
    }

    // Clean up
    printf("Free handles.\n");
    ret = SQLDisconnect( hdlDbc );
    if(!SQL_SUCCEEDED(ret)) {
        printf("Error disconnecting. Transaction still open?\n");
        exit(EXIT_FAILURE);
    }
    SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    exit(EXIT_SUCCESS);
}

运行以上代码后的结果如下所示。

Allocated an environment handle.
Set application to ODBC 3.
Allocated Database handle.
Connecting to database.
Connected to database.
Creating prepared statement
Created prepared statement.
Bound CustIDs array to prepared statement
Bound CustNames array to prepared statement
Bound phoneNums array to prepared statement
Adding Batch #1...successful.
Adding Batch #2...successful.
Adding Batch #3...successful.
Adding Batch #4...successful.
Adding Batch #5...successful.
Committing transaction
Committed transaction
Free handles.

结果表如下所示:

=> SELECT * FROM customers;
 CustID |  CustName   |  Phone_Number
--------+-------------+-----------------
    100 | Allen, Anna | 1-617-555-1234
    101 | Brown, Bill | 1-781-555-1212
    102 | Chu, Cindy  | 1-508-555-4321
    103 | Dodd, Don   | 1-617-555-4444
    100 | Allen, Anna | 1-617-555-1234
    101 | Brown, Bill | 1-781-555-1212
    102 | Chu, Cindy  | 1-508-555-4321
    103 | Dodd, Don   | 1-617-555-4444
    100 | Allen, Anna | 1-617-555-1234
    101 | Brown, Bill | 1-781-555-1212
    102 | Chu, Cindy  | 1-508-555-4321
    103 | Dodd, Don   | 1-617-555-4444
    100 | Allen, Anna | 1-617-555-1234
    101 | Brown, Bill | 1-781-555-1212
    102 | Chu, Cindy  | 1-508-555-4321
    103 | Dodd, Don   | 1-617-555-4444
    100 | Allen, Anna | 1-617-555-1234
    101 | Brown, Bill | 1-781-555-1212
    102 | Chu, Cindy  | 1-508-555-4321
    103 | Dodd, Don   | 1-617-555-4444
(20 rows)

2.3.1.13.3.1 - 跟踪加载状态 (ODBC)

加载一批数据之后,客户端应用程序可以获取已处理的行数并确定每个是被接受还是被拒绝。

确定接受的行的数量

若要获取某个批所处理的行数,请将名为 SQL_ATTR_PARAMS_PROCESSED_PTR 且指向用于接收行数的变量的属性添加到语句对象中:

    SQLULEN rowsProcessed;
    SQLSetStmtAttr(hdlStmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &rowsProcessed, 0);

当应用程序调用 SQLExecute() 以插入该批时,Vertica ODBC 驱动程序会将已处理的行数(不一定等于已成功插入的行数)保存到您在 SQL_ATTR_PARAMS_PROCESSED_PTR 语句属性中指定的变量。

查找接受的行和拒绝的行

应用程序还可以设置名为 SQL_ATTR_PARAM_STATUS_PTR 且指向一个数组(ODBC 驱动程序可以将插入每个行的结果存储在此数组中)的语句属性:

    SQLUSMALLINT   rowResults[ NUM_ENTRIES ];
    SQLSetStmtAttr(hdlStmt, SQL_ATTR_PARAM_STATUS_PTR, rowResults, 0);

该数组至少必须与每个批中要插入的行数一样大。

当应用程序调用 SQLExecute 以插入一个批时,ODBC 驱动程序会使用值填充该数组,这些值指示每个行已成功插入(SQL_PARAM_SUCCESS 或 SQL_PARAM_SUCCESS_WITH_INFO)还是遇到了错误 (SQL_PARAM_ERROR)。

以下示例扩展了使用批量插入中所示的示例,以便同时报告已处理的行数和插入的每个行的状态。

// Some standard headers
#include <stdio.h>
#include <stdlib.h>
// Only needed for Windows clients
// #include <windows.h>
// Standard ODBC headers
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
// Helper function to print SQL error messages.
template <typename HandleT>
void reportError(int handleTypeEnum, HandleT hdl)
{
    // Get the status records.
    SQLSMALLINT   i, MsgLen;
    SQLRETURN ret2;
    SQLCHAR       SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH];
    SQLINTEGER    NativeError;
    i = 1;
    printf("\n");
    while ((ret2 = SQLGetDiagRec(handleTypeEnum, hdl, i, SqlState, &NativeError,
        Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) {
            printf("error record %d\n", i);
            printf("sqlstate: %s\n", SqlState);
            printf("detailed msg: %s\n", Msg);
            printf("native error code: %d\n\n", NativeError);
            i++;
    }
}
int main()
{
    // Number of data rows to insert
    const int NUM_ENTRIES = 4;


    SQLRETURN ret;
    SQLHENV hdlEnv;
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate a handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated an environment handle.\n");
    }
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
        (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not set application version to ODBC3.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Set application to ODBC 3.\n");
    }
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate database handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated Database handle.\n");
    }
    // Connect to the database
    printf("Connecting to database.\n");
    const char *dsnName = "ExampleDB";
    const char* userID = "dbadmin";
    const char* passwd = "password123";
    ret = SQLConnect(hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS,(SQLCHAR*)userID,SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not connect to database.\n");
        reportError<SQLHDBC>(SQL_HANDLE_DBC, hdlDbc);
        exit(EXIT_FAILURE);
    } else {
        printf("Connected to database.\n");
    }
    // Set up a statement handle
    SQLHSTMT hdlStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);
    SQLExecDirect(hdlStmt, (SQLCHAR*)"DROP TABLE IF EXISTS customers",
        SQL_NTS);
    // Create a table into which we can store data
    printf("Creating table.\n");
    ret = SQLExecDirect(hdlStmt, (SQLCHAR*)"CREATE TABLE customers "
        "(CustID int, CustName varchar(50), Phone_Number char(15));",
        SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        reportError<SQLHDBC>( SQL_HANDLE_STMT, hdlStmt );
        exit(EXIT_FAILURE);
    } else {
        printf("Created table.\n");
    }
    // Create the prepared statement. This will insert data into the
    // table we created above.
    printf("Creating prepared statement\n");
    ret = SQLPrepare (hdlStmt, (SQLTCHAR*)"INSERT INTO customers (CustID, "
        "CustName,  Phone_Number) VALUES(?,?,?)", SQL_NTS) ;
    if(!SQL_SUCCEEDED(ret)) {
        reportError<SQLHDBC>( SQL_HANDLE_STMT, hdlStmt );
        exit(EXIT_FAILURE);
    } else {
        printf("Created prepared statement.\n");
    }
    // This is the data to be inserted into the database.
    char custNames[][50] = { "Allen, Anna", "Brown, Bill", "Chu, Cindy",
        "Dodd, Don" };
    SQLINTEGER custIDs[] = { 100, 101, 102, 103};
    char phoneNums[][15] = {"1-617-555-1234", "1-781-555-1212",
        "1-508-555-4321", "1-617-555-4444"};
    // Bind the data arrays to the parameters in the prepared SQL
    // statement
    ret = SQLBindParameter(hdlStmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
        0, 0, (SQLPOINTER)custIDs, sizeof(SQLINTEGER) , NULL);
    if(!SQL_SUCCEEDED(ret)) {
        reportError<SQLHDBC>( SQL_HANDLE_STMT, hdlStmt );
        exit(EXIT_FAILURE);
    } else {
        printf("Bound CustIDs array to prepared statement\n");
    }
    // Bind CustNames
    SQLBindParameter(hdlStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR,
        50, 0, (SQLPOINTER)custNames, 50, NULL);
    if(!SQL_SUCCEEDED(ret)) {
        reportError<SQLHDBC>( SQL_HANDLE_STMT, hdlStmt );
        exit(EXIT_FAILURE);
    } else {
        printf("Bound CustNames array to prepared statement\n");
    }
    // Bind phoneNums
    SQLBindParameter(hdlStmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
        15, 0, (SQLPOINTER)phoneNums, 15, NULL);
    if(!SQL_SUCCEEDED(ret)) {
        reportError<SQLHDBC>( SQL_HANDLE_STMT, hdlStmt );
        exit(EXIT_FAILURE);
    } else {
        printf("Bound phoneNums array to prepared statement\n");
    }
    // Set up a variable to recieve number of parameters processed.
    SQLULEN rowsProcessed;
    // Set a statement attribute to point to the variable
    SQLSetStmtAttr(hdlStmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &rowsProcessed, 0);
    // Set up an array to hold the result of each row insert
    SQLUSMALLINT   rowResults[ NUM_ENTRIES ];
    // Set a statement attribute to point to the array
    SQLSetStmtAttr(hdlStmt, SQL_ATTR_PARAM_STATUS_PTR, rowResults, 0);
    // Tell the ODBC driver how many rows we have in the
    // array.
    SQLSetStmtAttr(hdlStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)NUM_ENTRIES, 0);
    // Add multiple batches to the database. This just adds the same
    // batch of data over and over again for simplicity's sake.
    for (int batchLoop=1; batchLoop<=5; batchLoop++) {
        // Execute the prepared statement, loading all of the data
        // in the arrays.
        printf("Adding Batch #%d...", batchLoop);
        ret = SQLExecute(hdlStmt);
        if(!SQL_SUCCEEDED(ret)) {
            reportError<SQLHDBC>( SQL_HANDLE_STMT, hdlStmt );
            exit(EXIT_FAILURE);
        }
        // Number of rows processed is in rowsProcessed
        printf("Params processed: %d\n", rowsProcessed);
        printf("Results of inserting each row:\n");
        int i;
        for (i = 0; i<NUM_ENTRIES; i++) {
            SQLUSMALLINT result = rowResults[i];
            switch(rowResults[i]) {
                case SQL_PARAM_SUCCESS:
                case SQL_PARAM_SUCCESS_WITH_INFO:
                    printf("  Row %d inserted successsfully\n", i+1);
                    break;
                case SQL_PARAM_ERROR:
                    printf("  Row %d was not inserted due to an error.", i+1);
                    break;
                default:
                    printf("  Row %d had some issue with it: %d\n", i+1, result);
            }
        }
    }
    // Done with batches, commit the transaction
    printf("Commit Transaction\n");
    ret = SQLEndTran(SQL_HANDLE_DBC, hdlDbc, SQL_COMMIT);
    if(!SQL_SUCCEEDED(ret)) {
        reportError<SQLHDBC>( SQL_HANDLE_STMT, hdlStmt );
    }


    // Clean up
    printf("Free handles.\n");
    ret = SQLDisconnect( hdlDbc );
    if(!SQL_SUCCEEDED(ret)) {
        printf("Error disconnecting. Transaction still open?\n");
        exit(EXIT_FAILURE);
    }
    SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    exit(EXIT_SUCCESS);
}

运行以上示例代码后,将生成以下输出:

Allocated an environment handle.Set application to ODBC 3.
Allocated Database handle.
Connecting to database.
Connected to database.
Creating table.
Created table.
Creating prepared statement
Created prepared statement.
Bound CustIDs array to prepared statement
Bound CustNames array to prepared statement
Bound phoneNums array to prepared statement
Adding Batch #1...Params processed: 4
Results of inserting each row:
  Row 1 inserted successfully
  Row 2 inserted successfully
  Row 3 inserted successfully
  Row 4 inserted successfully
Adding Batch #2...Params processed: 4
Results of inserting each row:
  Row 1 inserted successfully
  Row 2 inserted successfully
  Row 3 inserted successfully
  Row 4 inserted successfully
Adding Batch #3...Params processed: 4
Results of inserting each row:
  Row 1 inserted successfully
  Row 2 inserted successfully
  Row 3 inserted successfully
  Row 4 inserted successfully
Adding Batch #4...Params processed: 4
Results of inserting each row:
  Row 1 inserted successfully
  Row 2 inserted successfully
  Row 3 inserted successfully
  Row 4 inserted successfully
Adding Batch #5...Params processed: 4
Results of inserting each row:
  Row 1 inserted successfully
  Row 2 inserted successfully
  Row 3 inserted successfully
  Row 4 inserted successfully
Commit Transaction
Free handles.

2.3.1.13.3.2 - 批量加载过程中的错误处理

加载各个批时,您可以查找有关已接受的行数和已拒绝的行的信息(有关详细信息,请参阅跟踪加载状态 (ODBC))。插入各个批时,不会发生其他错误(例如磁盘空间错误)。此行为是由单个 COPY 语句对多个连续批执行加载导致的。使用单个 COPY 语句可使批量加载过程更快执行。只有 COPY 语句关闭时,才会提交批量数据,而且 Vertica 会报告其他类型错误。

批量加载应用程序应在 COPY 语句关闭时检查错误。一般情况下,可以通过调用 SQLEndTran() 函数以结束事务来强制 COPY 语句关闭。您还可以通过以下方法强制 COPY 语句关闭:使用 SQLCloseCursor() 函数关闭光标,或者在加载中插入最后一批之前将数据库连接的 AutoCommit 属性设置为 true。

2.3.1.13.4 - 使用 COPY 语句

COPY 允许您将存储在数据库节点上的文件中的数据批量加载到 Vertica 数据库。此方法是将数据加载到 Vertica 的最高效方法,因为文件驻留在数据库服务器上。您必须是超级用户才能使用 COPY 访问数据库节点的文件系统。

以下示例演示了如何使用 COPY 命令。

// Some standard headers
#include <stdio.h>
#include <stdlib.h>
// Only needed for Windows clients
// #include <windows.h>
// Standard ODBC headers
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
// Helper function to determine if an ODBC function call returned
// successfully.
bool notSuccess(SQLRETURN ret) {
    return (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO);
}
int main()
{
    // Set up the ODBC environment
    SQLRETURN ret;
    SQLHENV hdlEnv;
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    if(notSuccess(ret)) {
        printf("Could not allocate a handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated an environment handle.\n");
    }
    // Tell ODBC that the application uses ODBC 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
        (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    if(notSuccess(ret)) {
        printf("Could not set application version to ODBC3.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Set application to ODBC 3.\n");
    }
    // Allocate a database handle.
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    // Connect to the database
    printf("Connecting to database.\n");
    const char *dsnName = "ExampleDB";

    // Note: User MUST be a database superuser to be able to access files on the
    // filesystem of the node.
    const char* userID = "dbadmin";
    const char* passwd = "password123";
    ret = SQLConnect(hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS,(SQLCHAR*)userID,SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if(notSuccess(ret)) {
        printf("Could not connect to database.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Connected to database.\n");
    }

    // Disable AUTOCOMMIT
    printf("Disabling autocommit.\n");
    ret = SQLSetConnectAttr(hdlDbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, SQL_NTS);
    if(notSuccess(ret)) {
        printf("Could not disable autocommit.\n");
        exit(EXIT_FAILURE);
    }

    // Set up a statement handle
    SQLHSTMT hdlStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);
    // Create table to hold the data
    SQLExecDirect(hdlStmt, (SQLCHAR*)"DROP TABLE IF EXISTS customers",
        SQL_NTS);
    SQLExecDirect(hdlStmt, (SQLCHAR*)"CREATE TABLE customers"
        "(Last_Name char(50) NOT NULL, First_Name char(50),Email char(50), "
        "Phone_Number char(15));",
        SQL_NTS);

    // Run the copy command to load data.
    ret=SQLExecDirect(hdlStmt, (SQLCHAR*)"COPY customers "
        "FROM '/data/customers.txt'",
        SQL_NTS);
    if(notSuccess(ret)) {
        printf("Data was not successfully loaded.\n");
        exit(EXIT_FAILURE);
    } else {
        // Get number of rows added.
        SQLLEN numRows;
        ret=SQLRowCount(hdlStmt, &numRows);
        printf("Successfully inserted %d rows.\n", numRows);

    }

    // Done with batches, commit the transaction
    printf("Committing transaction\n");
    ret = SQLEndTran(SQL_HANDLE_DBC, hdlDbc, SQL_COMMIT);
    if(notSuccess(ret)) {
        printf("Could not commit transaction\n");
    }  else {
        printf("Committed transaction\n");
    }

    // Clean up
    printf("Free handles.\n");
    SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    exit(EXIT_SUCCESS);
}

该示例在运行后输出了以下内容:

Allocated an environment handle.
Set application to ODBC 3.
Connecting to database.
Connected to database.
Disabling autocommit.
Successfully inserted 10001 rows.
Committing transaction
Committed transaction
Free handles.

2.3.1.13.5 - 使用 COPY LOCAL 从客户端进行流式数据传输

COPY LOCAL 将数据从客户端系统文件流式传输到 Vertica 数据库。此语句通过 ODBC 驱动程序进行工作,从而简化了将数据文件从客户端传输到服务器的任务。

COPY LOCAL 通过 ODBC 驱动程序以透明方式工作。当客户端应用程序执行 COPY LOCAL 语句时,ODBC 驱动程序将从客户端读取数据文件并将其流式传输到服务器。

此示例演示如何使用 COPY LOCAL 语句从客户端系统加载数据:

// Some standard headers
#include <stdio.h>
#include <stdlib.h>
// Only needed for Windows clients
// #include <windows.h>
// Standard ODBC headers
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
int main()
{
    // Set up the ODBC environment
    SQLRETURN ret;
    SQLHENV hdlEnv;
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not allocate a handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Allocated an environment handle.\n");
    }
    // Tell ODBC that the application uses ODBC 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
        (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not set application version to ODBC3.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Set application to ODBC 3.\n");
    }
    // Allocate a database handle.
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not aalocate a database handle.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Set application to ODBC 3.\n");
    }
    // Connect to the database
    printf("Connecting to database.\n");
    const char *dsnName = "ExampleDB";
    const char* userID = "dbadmin";
    const char* passwd = "password123";
    ret = SQLConnect(hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS,(SQLCHAR*)userID,SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not connect to database.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("Connected to database.\n");
    }

    // Set up a statement handle
    SQLHSTMT hdlStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);


    // Create table to hold the data
    SQLExecDirect(hdlStmt, (SQLCHAR*)"DROP TABLE IF EXISTS customers",
        SQL_NTS);
    SQLExecDirect(hdlStmt, (SQLCHAR*)"CREATE TABLE customers"
        "(Last_Name char(50) NOT NULL, First_Name char(50),Email char(50), "
        "Phone_Number char(15));",
        SQL_NTS);

    // Run the copy command to load data.
    ret=SQLExecDirect(hdlStmt, (SQLCHAR*)"COPY customers "
        "FROM LOCAL '/home/dbadmin/customers.txt'",
        SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Data was not successfully loaded.\n");
        exit(EXIT_FAILURE);
    } else {
        // Get number of rows added.
        SQLLEN numRows;
        ret=SQLRowCount(hdlStmt, &numRows);
        printf("Successfully inserted %d rows.\n", numRows);
    }

    // COPY commits automatically, unless it is told not to, so
    // there is no need to commit the transaction.

    // Clean up
    printf("Free handles.\n");
    ret = SQLDisconnect( hdlDbc );
    if(!SQL_SUCCEEDED(ret)) {
        printf("Error disconnecting. Transaction still open?\n");
        exit(EXIT_FAILURE);
    }
    SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
    SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    exit(EXIT_SUCCESS);
}

除了使用 COPY 语句的 LOCAL 选项从客户端系统而非数据库节点的文件系统加载数据之外,此示例与使用 COPY 语句中所示的示例基本相同。

2.3.2 - C#

使用适用于 ADO.NET 的 Vertica 驱动程序,采用 C# 编写的应用程序可以从 Vertica 数据库读取数据以及更新其中的数据和将数据加载到其中。此驱动程序提供了一个数据适配器(Vertica 数据适配器),该数据适配器有助于从数据库将数据读取到数据集中,还有助于将数据集中已更改的数据写回数据库。此驱动程序还提供用于读取数据的数据读取器 (VerticaDataReader)。此驱动程序需要 .NET Framework 版本 3.5 以上。

有关 ADO.NET 的详细信息,请参阅:

先决条件

创建 C# 客户端应用程序之前,必须安装 ADO.NET 客户端驱动程序

2.3.2.1 - ADO.NET 数据类型

下表详细介绍了 Vertica 数据类型与 .NET 数据类型和 ADO.NET 数据类型之间的映射。

UUID 向后兼容性

Vertica 9.0.0 版引入了 UUID 数据类型,包括对 UUID 的 JDBC 支持。Vertica ADO.NET、ODBC 和 OLE DB 客户端在 9.0.1 版中添加了对 UUID 的完全支持。Vertica 保持与不支持 UUID 数据类型的旧 受支持 客户端驱动程序版本的向后兼容性,如下所示:

2.3.2.2 - 设置 ADO.NET 会话的区域设置

  • ADO.NET 应用程序使用 UTF-16 字符集编码,并负责将任何非 UTF-16 编码数据转换为 UTF-16。针对 ODBC 的相同警告在违反此编码时适用。

  • 向 Vertica 服务器传递数据时,ADO.NET 驱动程序会将 UTF-16 数据转换为 UTF-8,并且会将 Vertica 服务器发送的数据从 UTF-8 转换为 UTF-16。

  • ADO.NET 应用程序应通过执行 SET LOCALE TO 命令来设置正确的服务器会话区域设置,以便获取预期的排序规则和服务器上的字符串函数行为。

  • 如果在数据库级别没有默认会话区域设置,则 ADO.NET 应用程序需要通过执行 SET LOCALE TO 命令来设置正确的服务器会话区域设置,以便获取预期的排序规则和服务器上的字符串函数行为。请参阅 SET LOCALE 命令。

2.3.2.3 - 连接到数据库

2.3.2.3.1 - 使用 TLS:在 Windows 中安装证书

您可以选择使用 TLS 来保护 ADO.NET 应用程序和 Vertica 之间的通信。Vertica ADO.NET 驱动程序在查找 TLS 证书时使用默认的 Windows 密钥库。该密钥库与 Internet Explorer 使用的密钥库相同。

在客户端上使用 TLS 之前,您必须在服务器上实施 TLS。请参阅 TLS 协议,执行其中的步骤,然后返回到本主题以在 Windows 上安装 TLS 证书。

要对 ADO.NET 和 Vertica 的连接使用 TLS:

  • 将服务器证书和客户端证书导入到 Windows 密钥库。

  • 导入证书颁发机构 (Certifying Authority, CA) 的公用证书(如果证书要求执行此操作)。

将服务器证书和客户端证书导入到 Windows 密钥库:

  1. 将在服务器上启用 TLS 时生成的 server.crt 文件复制到 Windows 计算机。

  2. 双击该证书。

  3. 让 Windows 确定密钥类型,然后单击安装

导入 CA 的公用证书:

必须为证书建立信任链。您可能需要导入 CA 的公用证书(尤其是当该证书是自签名证书时)。

  1. 使用以上过程中的同一个证书,双击该证书。

  2. 选择将所有的证书都放入下列存储

  3. 单击浏览 (Browse),选择受信任的根证书颁发机构 (Trusted Root Certification Authorities),然后单击下一步 (Next)

  4. 单击安装 (Install)

在 ADO.NET 应用程序中启用 SSL

在连接字符串中,务必通过将 VerticaConnectionStringBuilder 中的 SSL 属性设置为 true 来启用 SSL,例如:

//configure connection properties    VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
    builder.Host = "192.168.17.10";
    builder.Database = "VMart";
    builder.User = "dbadmin";
    builder.SSL = true;
    //open the connection
    VerticaConnection _conn = new VerticaConnection(builder.ToString());
    _conn.Open();

2.3.2.3.2 - 打开和关闭数据库连接 (ADO.NET)

通过 ADO.NET 访问 Vertica 中的数据之前,您必须使用 VerticaConnection 类(此类是 System.Data.DbConnection 的实施)创建与数据库的连接。VerticaConnection 使用包含连接属性作为字符串的单个参数。您可以手动创建属性关键字的字符串以用作参数,或者也可以使用 VerticaConnectionStringBuilder 类构建连接字符串。

要下载 ADO.NET 驱动程序,请转到客户端驱动程序下载页面

本主题详细介绍以下内容:

  • 手动构建连接字符串和连接到 Vertica

  • 使用 VerticaConnectionStringBuilder 创建连接字符串和连接到 Vertica

  • 关闭连接

要手动创建连接字符串:

请参阅 ADO.NET 连接属性,了解在连接字符串中使用的可用属性列表。至少要指定主机、数据库和用户。

  1. 为每个属性提供一个值,并依次附加各个属性和值(用分号分隔)。将此字符串分配给变量。例如:

    String connectString = "DATABASE=VMart;HOST=v_vmart_node0001;USER=dbadmin";
    
  2. 构建指定了连接字符串的 Vertica 连接对象。

    VerticaConnection _conn = new VerticaConnection(connectString)
    
  3. 打开连接。

    _conn.Open();
    
  4. 创建命令对象,并将它与连接相关联。所有 VerticaCommand 对象都必须与连接相关联。

    VerticaCommand command = _conn.CreateCommand();
    

要使用 VerticaConnectionStringBuilder 类创建连接字符串并打开连接:

  1. 创建 VerticaConnectionStringBuilder 类的新对象。

    VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
    
  2. 使用属性值更新 VerticaConnectionStringBuilder 对象。请参阅 ADO.NET 连接属性,了解在连接字符串中使用的可用属性列表。至少要指定主机、数据库和用户。

    builder.Host = "v_vmart_node0001";
    builder.Database = "VMart";
    builder.User = "dbadmin";
    
  3. 构建将 VerticaConnectionStringBuilder 连接对象指定为字符串的 Vertica 连接对象。

    VerticaConnection _conn = new VerticaConnection(builder.ToString());
    
  4. 打开连接。

    _conn.Open();
    
  5. 创建命令对象,并将它与连接相关联。所有 VerticaCommand 对象都必须与连接相关联。

    VerticaCommand command = _conn.CreateCommand;
    

要关闭连接:

使用完数据库后,关闭连接。如未关闭连接,应用程序的性能和可扩展性会降低。还会导致其他客户端无法获取锁。

 _conn.Close();

示例用法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();
        //Perform some operations
            _conn.Close();
        }
    }
}

2.3.2.3.3 - ADO.NET 连接属性

要下载 ADO.NET 驱动程序,请转到客户端驱动程序下载页面

可以使用连接属性来配置 ADO.NET 客户端应用程序和 Vertica 数据库之间的连接。属性提供有关连接的基本信息,例如,连接到数据库所需的服务器名称和端口号。

您可以通过两种方法设置连接属性:

  • 包含属性名称和值以作为传递到 VerticaConnection 的连接字符串的一部分。

  • VerticaConnectionStringBuilder 对象中设置属性,然后将该对象作为字符串传递到 VerticaConnection

2.3.2.3.4 - ADO.NET 中的负载均衡

本机连接负载均衡

本机连接负载均衡有助于在 Vertica 数据库中的主机上分散客户端连接所带来的开销。服务器和客户端都必须启用本机连接负载均衡。如果两者者都启用了本机负载均衡,则当客户端最初连接到数据库中的主机时,此主机会从数据库中当前正在运行的主机列表中选择一个主机来处理客户端连接,并通知客户端它所选择的主机。

如果最初联系的主机没有选择自身来处理连接,则客户端会断开连接,然后打开指向第一个主机所选主机的另一个连接。指向此第二个主机的连接进程将照常进行—如果启用了 SSL,则会启动 SSL 协商,否则客户端会启动身份验证过程。有关详细信息,请参阅关于本机连接负载均衡

若要在客户端上启用本机连接负载均衡,请在连接字符串中将 ConnectionLoadBalance 连接参数设置为 true,或者使用 ConnectionStringBuilder() 进行此设置。以下示例说明了如何在本机连接负载均衡已启用的情况下多次连接到数据库,以及从 V_MONITOR.CURRENT_SESSION 系统表获取处理连接的节点的名称。

using System;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;

namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "v_vmart_node0001.example.com";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            // Enable native client load balancing in the client,
            // must also be enabled on the server!
            builder.ConnectionLoadBalance = true;

            // Connect 3 times to verify a new node is connected
            // for each connection.
            for (int i = 1; i <= 4; i++) {
                try {
                    VerticaConnection _conn = new VerticaConnection(builder.ToString());
                    _conn.Open();
                    if (i == 1) {
                        // On the first connection, check the server policy for load balance
                        VerticaCommand sqlcom = _conn.CreateCommand();
                        sqlcom.CommandText = "SELECT LOAD_BALANCE_POLICY FROM V_CATALOG.DATABASES";
                        var returnValue = sqlcom.ExecuteScalar();
                        Console.WriteLine("Status of load balancy policy
             on server: " + returnValue.ToString() + "\n");
                    }
                    VerticaCommand command = _conn.CreateCommand();
                    command.CommandText = "SELECT node_name FROM V_MONITOR.CURRENT_SESSION";
                    VerticaDataReader dr = command.ExecuteReader();
                    while (dr.Read()) {
                        Console.Write("Connect attempt #" + i + "... ");
                        Console.WriteLine("Connected to node " + dr[0]);
                    }
                    dr.Close();
                    _conn.Close();
                    Console.WriteLine("Disconnecting.\n");
                }
                catch(Exception e) {
                    Console.WriteLine(e.Message);
                }
            }
        }
    }
}

运行上述示例得到了以下输出:

Status of load balancing policy on server: roundrobin

Connect attempt #1... Connected to node v_vmart_node0001
Disconnecting.

Connect attempt #2... Connected to node v_vmart_node0002
Disconnecting.

Connect attempt #3... Connected to node v_vmart_node0003
Disconnecting.

Connect attempt #4... Connected to node v_vmart_node0001
Disconnecting.

基于主机名的负载均衡

您还可以通过将单个主机名解析为多个 IP 地址来平衡工作负载。ADO.NET 客户端驱动程序通过自动将主机名随机解析为指定的 IP 地址之一来实现负载均衡。

例如,假设主机名 verticahost.example.comC:\Windows\System32\drivers\etc\hosts 中有以下条目:

192.0.2.0 verticahost.example.com
192.0.2.1 verticahost.example.com
192.0.2.2 verticahost.example.com

指定主机名 verticahost.example.com 会随机解析为列出的 IP 地址之一。

2.3.2.3.5 - ADO.NET 连接故障转移

如果客户端应用程序尝试连接到 Vertica 群集中处于关闭状态的主机,则使用默认连接配置时连接尝试将会失败。此故障通常会向用户返回一个错误。用户必须等待主机恢复并重试连接,或者手动编辑连接设置以选择其他主机。

由于 Vertica 分析型数据库采用分布式架构,通常您不会关注哪个数据库主机处理客户端应用程序的连接。可以使用客户端驱动程序的连接故障转移功能来防止用户在无法访问连接设置中所指定主机的情况下收到连接错误。JDBC 驱动程序可通过几种方式让客户端驱动程序在无法访问连接参数中所指定的主机时自动尝试连接到其他主机:

  • 将 DNS 服务器配置为返回一个主机名的多个 IP 地址。在连接设置中使用此主机名时,客户端会尝试连接到 DNS 找到的第一个 IP 地址。如果位于该 IP 地址的主机无法访问,则客户端会尝试连接到第二个 IP,依此类推,直到其成功地连接到某个主机或试过所有 IP 地址为止。

  • 提供当您在连接参数中指定的主要主机无法访问时客户端驱动程序尝试连接的备份主机列表。

  • (仅限 JDBC)在尝试连接到下一个节点之前,使用特定于驱动程序的连接属性来管理超时。

在所有方法中,故障转移过程对于客户端应用程序是透明的(除了在选择使用列表故障转移方法时需指定备份主机列表之外)。如果主要主机无法访问,客户端驱动程序会自动尝试连接到其他主机。

故障转移仅适用于初次建立客户端连接的情况。如果连接断开,驱动程序不会自动尝试重新连接到数据库中的其他主机。

通常可选择上述两种故障转移方法中的一种。但它们可以一起使用。如果您的 DNS 服务器返回多个 IP 地址,并且您提供了备份主机列表,则客户端会首先尝试连接 DNS 服务器返回的所有 IP,然后再尝试连接备份列表中的主机。

DNS 故障转移方法可集中处理配置客户端故障转移。在向 Vertica 分析型数据库群集添加新节点时,可以选择通过编辑 DNS 服务器设置来将这些节点添加到故障转移列表中。所有使用 DNS 服务器连接到 Vertica 分析型数据库的客户端系统都会自动采用连接故障转移,而无需更改任何设置。但此方法需要对所有客户端用于连接到 Vertica 分析型数据库群集的 DNS 服务器具有管理访问权限。这在贵组织中可能无法实现。

使用备份服务器列表要比编辑 DNS 服务器设置更加容易。但此方法不能集中处理故障转移功能。如果更改了 Vertica 分析型数据库群集,您可能需要在每个客户端系统上更新应用程序设置。

使用 DNS 故障转移

若要使用 DNS 故障转移,您需要更改 DNS 服务器的设置,将单一主机名映射为 Vertica 分析型数据库群集中主机的多个 IP 地址。然后让所有客户端应用程序都使用该主机名连接到 Vertica 分析型数据库。

您可以选择让 DNS 服务器为主机名返回任何所需数量的 IP 地址。在小型群集中,您可以选择让其返回群集中所有主机的 IP 地址。但对于大型群集,应考虑选择返回一部分主机。否则可能会导致长时间延迟,因为客户端驱动程序尝试连接到数据库中处于关闭状态的每个主机会失败。

使用备份主机列表

若要启用基于备份列表的连接故障转移,需要在客户端应用程序的 BackupServerNode 参数中指定主机的至少一个 IP 地址或主机名。可以视情况在主机名或 IP 后面使用冒号和端口号。如果未提供端口号,驱动程序会默认使用标准 Vertica 端口号 (5433)。若要列出多个主机,请用逗号分隔这些主机。

以下示例演示了如何通过设置 BackupServerNode 连接参数来指定可尝试连接的其他主机。由于有意在连接字符串中使用了一个不存在的节点,因此初始连接失败。客户端驱动程序必须尝试通过备份主机来与 Vertica 建立连接。

using System;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder =
        new VerticaConnectionStringBuilder();
            builder.Host = "not.a.real.host:5433";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            builder.BackupServerNode =
        "another.broken.node:5433,v_vmart_node0002.example.com:5433";
            try
            {
                VerticaConnection _conn =
            new VerticaConnection(builder.ToString());
                _conn.Open();
                VerticaCommand sqlcom = _conn.CreateCommand();
                sqlcom.CommandText = "SELECT node_name FROM current_session";
                var returnValue = sqlcom.ExecuteScalar();
                Console.WriteLine("Connected to node: " +
            returnValue.ToString() + "\n");
                _conn.Close();
                Console.WriteLine("Disconnecting.\n");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

注意

  • 启用本机连接负载均衡时,在 BackupServerNode 连接参数中指定的其他服务器将只用于指向 Vertica 主机的初始连接。如果主机将客户端重定向至数据库群集中的其他主机来处理其连接请求,则第二个连接将不使用备份节点列表。这很少出现问题,因为本机连接负载均衡知道数据库中的哪个节点目前正处于运行状态。 请参阅ADO.NET 中的负载均衡

  • 从 BackupServerNode 列表中获取的与主机的连接未池化以用于 ADO.NET 连接。

2.3.2.4 - 使用 ADO.NET 查询数据库

本节介绍如何创建查询以执行下列操作:

2.3.2.4.1 - 插入数据 (ADO.NET)

可以使用 VerticaCommand 类完成插入数据。VerticaCommand 是 DbCommand 的实施。使用它可以创建 SQL 语句以及向数据库发送该语句。使用 CommandText 方法将某个 SQL 语句分配给命令,然后通过调用 ExecuteNonQuery 方法来执行该 SQL 语句。ExecuteNonQuery 方法用于执行不返回结果集的语句。

要插入一行数据:

  1. 创建与数据库的连接

  2. 使用连接创建命令对象。

    VerticaCommand command = _conn.CreateCommand();
    
  3. 使用 INSERT 语句插入数据。以下是简单插入操作的示例。请注意,它不包含 COMMIT 语句,因为 Vertica ADO.NET 驱动程序在自动提交模式下工作。

    command.CommandText =
         "INSERT into test values(2, 'username', 'email', 'password')";
    
  4. 执行查询。rowsAdded 变量包含由 INSERT 语句添加的行数。

    Int32 rowsAdded = command.ExecuteNonQuery();
    

    ExecuteNonQuery() 方法将返回受 UPDATE、INSERT 和 DELETE 语句的命令影响的行数。此方法对所有其他类型的语句返回 -1。如果发生回退,则此方法也设置为 -1。

示例用法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();
        VerticaCommand command = _conn.CreateCommand();
        command.CommandText =
               "INSERT into test values(2, 'username', 'email', 'password')";
        Int32 rowsAdded = command.ExecuteNonQuery();
        Console.WriteLine( rowsAdded + " rows added!");
            _conn.Close();
        }
    }
}

2.3.2.4.1.1 - 使用参数

可以使用参数高效地重复执行相似 SQL 语句。

使用参数

VerticaParameter 是 ADO.NET 中的 System.Data.DbParameter 基本类的扩展,用于在发送到服务器的命令中设置参数。可以在 WHERE 子句中的值不是静态值的所有查询 (SELECT/INSERT/UPDATE/DELETE) 中使用参数;此方式适用于包含已知列集但其筛选器条件由应用程序或最终用户动态设置的所有查询。以这种方式使用参数可大大降低出现 SQL 注入问题的可能性;即使只是从多个变量创建 SQL 查询,也会出现这种问题。

您需要为参数分配有效的 DbType、VerticaDbType 或系统类型。有关 System、Vertica 和 DbTypes 的映射,请参阅数据类型ADO.NET 数据类型

若要创建参数占位符,请在实际查询字符串中的参数名称前面放置电子邮件符号 (@) 或冒号 (:) 字符。请勿在占位符指示符(@ 或 :)和占位符之间插入任何空格。

例如,以下典型查询使用字符串 'MA' 作为筛选器。

SELECT customer_name, customer_address, customer_city, customer_state
FROM customer_dimension WHERE customer_state = 'MA';

相反,可以编写查询以使用参数。在以下示例中,字符串 MA 替换为参数占位符 @STATE。

SELECT customer_name, customer_address, customer_city, customer_state
FROM customer_dimension WHERE customer_state = @STATE;

例如,前一个示例的 ADO.net 代码编写如下:

VerticaCommand command = _conn.CreateCommand();
command.CommandText = “SELECT customer_name, customer_address, customer_city, customer_state
    FROM customer_dimension WHERE customer_state = @STATE”;
command.Parameters.Add(new VerticaParameter( “STATE”, VerticaType.VarChar));
command.Parameters["STATE"].Value = "MA";

2.3.2.4.1.2 - 创建和回退事务

创建事务

Vertica 中的事务具有以下特性:原子、一致、隔离和持久。使用 Vertica ADO.NET 驱动程序连接到数据库时,连接处于自动提交模式,并且每个查询会在执行后提交。可以将多个语句收集到单个事务,然后使用单个事务同时提交这些语句。如果代码确定不应提交事务,您还可以选择在提交事务之前回退该事务。

这些事务使用 VerticaTransaction 对象,该对象是 DbTransaction 的实施。您必须将事务与 VerticaCommand 对象相关联。

以下代码使用显式事务将行逐个插入到 VMart 架构的表中。

要使用 ADO.NET 驱动程序在 Vertica 中创建事务:

  1. 创建与数据库的连接

  2. 使用连接创建命令对象。

    VerticaCommand command = _conn.CreateCommand();
    
  3. 启动显式事务,并将命令与该事务相关联。

    VerticaTransaction txn = _conn.BeginTransaction();
    command.Connection = _conn;
    command.Transaction = txn;
    
  4. 执行各个 SQL 语句以添加行。

    command.CommandText =
         "insert into product_dimension values( ... )";
    command.ExecuteNonQuery();
    command.CommandText =
         "insert into store_orders_fact values( ... )";
    
  5. 提交事务。

    txn.Commit();
    

回退事务

如果代码检查到错误,则您可以捕获错误并回退整个事务。

VerticaTransaction txn = _conn.BeginTransaction();
VerticaCommand command = new
        VerticaCommand("insert into product_dimension values( 838929, 5, 'New item 5' )", _conn);
// execute the insert
command.ExecuteNonQuery();
command.CommandText = "insert into product_dimension values( 838929, 6, 'New item 6' )";
// try insert and catch any errors
bool error = false;
try
{
    command.ExecuteNonQuery();
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
    error = true;
}
if (error)
{
    txn.Rollback();
    Console.WriteLine("Errors. Rolling Back.");
}
else
{
    txn.Commit();
    Console.WriteLine("Queries Successful. Committing.");
}

提交和回退示例

以下示例详细介绍了如何在事务期间提交或回退查询。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();
            bool error = false;
                VerticaCommand command = _conn.CreateCommand();
                VerticaCommand command2 = _conn.CreateCommand();
                VerticaTransaction txn = _conn.BeginTransaction();
                command.Connection = _conn;
                command.Transaction = txn;
                command.CommandText =
                "insert into test values(1, 'test', 'test', 'test' )";
                Console.WriteLine(command.CommandText);
                try
                {
                    command.ExecuteNonQuery();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    error = true;
                }
                command.CommandText =
                "insert into test values(2, 'ear', 'eye', 'nose', 'extra' )";
                Console.WriteLine(command.CommandText);
                try
                {
                    command.ExecuteNonQuery();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    error = true;
                }
                if (error)
                {
                    txn.Rollback();
                    Console.WriteLine("Errors. Rolling Back.");
                }
                else
                {
                    txn.Commit();
                    Console.WriteLine("Queries Successful. Committing.");
                }
            _conn.Close();
        }
    }
}

该示例将在控制台上显示以下输出:

insert into test values(1, 'test', 'test', 'test' )
insert into test values(2, 'ear', 'eye', 'nose', 'extra' )
[42601]ERROR: INSERT has more expressions than target columns
Errors. Rolling Back.

另请参阅

2.3.2.4.1.2.1 - 设置事务隔离级别

可以按连接和事务设置事务隔离级别。有关 Vertica 中支持的事务隔离级别的概述,请参阅 事务。若要为连接设置默认事务隔离级别,请在 VerticaConnectionStringBuilder 字符串中使用 IsolationLevel 关键字(有关详细信息,请参阅连接字符串关键字)。若要为单个事务设置隔离级别,请将隔离级别传递到用于启动事务的 VerticaConnection.BeginTransaction() 方法调用。

要按连接设置隔离级别:

  1. 使用 VerticaConnectionStringBuilder 构建连接字符串。

  2. 为 IsolationLevel 生成器字符串提供一个值。此字符串可接受以下两个值之一:IsolationLevel.ReadCommited(默认)或 IsolationLevel.Serializeable。例如:

        VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
        builder.Host = "192.168.1.100";
        builder.Database = "VMart";
        builder.User = "dbadmin";
        builder.IsolationLevel = System.Data.IsolationLevel.Serializeable
        VerticaConnection _conn1 = new VerticaConnection(builder.ToString());
        _conn1.Open();
    

要按事务设置隔离级别:

  1. 在 BeginTransaction 方法上设置 IsolationLevel,例如

    VerticaTransaction txn = _conn.BeginTransaction(IsolationLevel.Serializable);
    

示例用法:

以下示例演示了下列操作:

  • 获取连接的事务隔离级别。

  • 使用连接属性设置连接的隔离级别。

  • 为新事务设置事务隔离级别。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            VerticaConnection _conn1 = new VerticaConnection(builder.ToString());
             _conn1.Open();
            VerticaTransaction txn1 = _conn1.BeginTransaction();
            Console.WriteLine("\n Transaction 1 Transaction Isolation Level: " +
             txn1.IsolationLevel.ToString());
            txn1.Rollback();
            VerticaTransaction txn2 = _conn1.BeginTransaction(IsolationLevel.Serializable);
            Console.WriteLine("\n Transaction 2 Transaction Isolation Level: " +
             txn2.IsolationLevel.ToString());
            txn2.Rollback();
            VerticaTransaction txn3 = _conn1.BeginTransaction(IsolationLevel.ReadCommitted);
            Console.WriteLine("\n Transaction 3 Transaction Isolation Level: " +
             txn3.IsolationLevel.ToString());
            _conn1.Close();
        }
    }
}

运行后,以上示例代码会将以下内容输出到系统控制台:

 Transaction 1 Transaction Isolation Level: ReadCommitted
 Transaction 2 Transaction Isolation Level: Serializable
 Transaction 3 Transaction Isolation Level: ReadCommitted

2.3.2.4.2 - 读取数据 (ADO.Net)

若要从数据库读取数据,请使用 VerticaDataReader(一种 DbDataReader 实施)。此实施可通过分析应用程序在服务器上运行,从而将大量数据快速移出服务器。

要使用 VerticaDataReader 从数据库读取数据:

  1. 创建与数据库的连接

  2. 使用连接创建命令对象。

        VerticaCommand command = _conn.CreateCommand();
    
  3. 创建查询。此查询与示例 VMart 数据库配合工作。

            command.CommandText =
            "SELECT fat_content, product_description " +
            "FROM (SELECT DISTINCT fat_content, product_description" +
            "      FROM product_dimension " +
            "      WHERE department_description " +        "      IN ('Dairy') " +
            "      ORDER BY fat_content) AS food " +
            "LIMIT 10;";
    
  4. 执行读取器以从查询返回结果。以下命令将调用 VerticaCommand 对象的 ExecuteReader 方法以获取 VerticaDataReader 对象。

    VerticaDataReader dr = command.ExecuteReader();
    
  5. 读取数据。数据读取器将在连续流中返回结果。因此,您必须逐行从表中读取数据。以下示例使用 while 循环来完成此操作。

     Console.WriteLine("\n\n Fat Content\t  Product Description");
         Console.WriteLine("------------\t  -------------------");
         int rows = 0;
         while (dr.Read())
         {
            Console.WriteLine("     " + dr[0] + "    \t  " + dr[1]);
            ++rows;
         }
         Console.WriteLine("------------\n  (" + rows + " rows)\n");
    
  6. 完成后,关闭数据读取器以释放资源。

        dr.Close();
    

示例用法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();
        VerticaCommand command = _conn.CreateCommand();
            command.CommandText =
                "SELECT fat_content, product_description " +
                "FROM (SELECT DISTINCT fat_content, product_description" +
                "      FROM product_dimension " +
                "      WHERE department_description " +
                "      IN ('Dairy') " +
                "      ORDER BY fat_content) AS food " +
                "LIMIT 10;";
          VerticaDataReader dr = command.ExecuteReader();

         Console.WriteLine("\n\n Fat Content\t  Product Description");
         Console.WriteLine("------------\t  -------------------");
         int rows = 0;
         while (dr.Read())
         {
                Console.WriteLine("     " + dr[0] + "    \t  " + dr[1]);
                ++rows;
         }
         Console.WriteLine("------------\n  (" + rows + " rows)\n");
              dr.Close();
            _conn.Close();
        }
    }
}

2.3.2.4.3 - 通过 ADO.Net 加载数据

此部分详细介绍可用于通过 ADO.NET 客户端驱动程序加载 Vertica 中的数据的各种方法:

2.3.2.4.3.1 - 使用 Vertica 数据适配器

Vertica 数据适配器 (VerticaDataAdapter) 可使客户端能够在数据集和 Vertica 数据库之间交换数据。该适配器是 DbDataAdapter 的实施。例如,您可以使用 VerticaDataAdapter 仅读取数据;或者也可以从数据库将数据读取到数据集中,然后将数据集中已更改的数据写回数据库。

批量更新

使用 Update() 方法更新数据集时,您可以选择在调用 Update() 之前使用 UpdateBatchSize() 方法,以便减少客户端在执行更新期间与服务器通信的次数。UpdateBatchSize 的默认值为 1。如果对数据集使用多个 rows.Add() 命令,则您可以将批大小更改为最佳大小以加快客户端完成更新所需执行的操作。

使用数据适配器从 Vertica 读取数据:

以下示例详细介绍如何对 VMart 架构执行 select 查询并将结果加载到 DataTable,然后将 DataTable 的内容输出到控制台。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();

            // Try/Catch any exceptions
                   try
            {
                using (_conn)
                {
                    // Create the command
                    VerticaCommand command = _conn.CreateCommand();
                    command.CommandText = "select product_key, product_description " +
                        "from product_dimension where product_key < 10";

                        // Associate the command with the connection
                        command.Connection = _conn;

                        // Create the DataAdapter
                        VerticaDataAdapter adapter = new VerticaDataAdapter();
                        adapter.SelectCommand = command;

                        // Fill the DataTable
                        DataTable table = new DataTable();
                        adapter.Fill(table);

                        //  Display each row and column value.
                        int i = 1;
                        foreach (DataRow row in table.Rows)
                        {
                            foreach (DataColumn column in table.Columns)
                            {
                                Console.Write(row[column] + "\t");
                            }
                            Console.WriteLine();
                            i++;
                        }
                    Console.WriteLine(i + " rows returned.");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            _conn.Close();
        }
    }
}

从 Vertica 将数据读取到数据集并更改数据:

以下示例显示了如何使用数据适配器从 VMart 架构的维度表读取数据以及将数据插入到其中。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using Vertica.Data.VerticaClient
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();

                  // Try/Catch any exceptions
                    try
            {
                using (_conn)
                {

                            //Create a data adapter object using the connection
                            VerticaDataAdapter da = new VerticaDataAdapter();

                            //Create a select statement that retrieves data from the table
                            da.SelectCommand = new
                        VerticaCommand("select * from product_dimension where product_key < 10",
                        _conn);
                            //Set up the insert command for the data adapter, and bind variables for some of the columns
                            da.InsertCommand = new
                        VerticaCommand("insert into product_dimension values( :key, :version, :desc )",
                        _conn);
                    da.InsertCommand.Parameters.Add(new VerticaParameter("key", VerticaType.BigInt));
                    da.InsertCommand.Parameters.Add(new VerticaParameter("version", VerticaType.BigInt));
                    da.InsertCommand.Parameters.Add(new VerticaParameter("desc", VerticaType.VarChar));
                    da.InsertCommand.Parameters[0].SourceColumn = "product_key";
                    da.InsertCommand.Parameters[1].SourceColumn = "product_version";
                    da.InsertCommand.Parameters[2].SourceColumn = "product_description";
                    da.TableMappings.Add("product_key", "product_key");
                    da.TableMappings.Add("product_version", "product_version");
                    da.TableMappings.Add("product_description", "product_description");

                            //Create and fill a Data set for this dimension table, and get the resulting DataTable.
                            DataSet ds = new DataSet();
                    da.Fill(ds, 0, 0, "product_dimension");
                    DataTable dt = ds.Tables[0];

                            //Bind parameters and add two rows to the table.
                            DataRow dr = dt.NewRow();
                    dr["product_key"] = 838929;
                    dr["product_version"] = 5;
                    dr["product_description"] = "New item 5";
                    dt.Rows.Add(dr);
                    dr = dt.NewRow();
                    dr["product_key"] = 838929;
                    dr["product_version"] = 6;
                    dr["product_description"] = "New item 6";
                    dt.Rows.Add(dr);
                    //Extract the changes for the added rows.
                            DataSet ds2 = ds.GetChanges();

                            //Send the modifications to the server.
                            int updateCount = da.Update(ds2, "product_dimension");

                           //Merge the changes into the original Data set, and mark it up to date.
                            ds.Merge(ds2);
                    ds.AcceptChanges();
                    Console.WriteLine(updateCount + " updates made!");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            _conn.Close();
        }
    }
}

2.3.2.4.3.2 - 使用批量插入和预定义的语句

可以使用带有参数的预定义的语句批量加载数据。如果遇到任何错误,您还可以使用事务回退批量加载。

如果要加载大批量数据(超过 100 MB),请考虑使用直接批量插入。

以下示例详细介绍使用包含在数组中的数据以及参数和事务来批量加载数据。

可以通过以下命令创建在此示例中使用的 test 表:

=> CREATE TABLE test (id INT, username VARCHAR(24), email VARCHAR(64), password VARCHAR(8));

使用参数和事务的示例批量插入


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();
            // Create arrays for column data
                    int[] ids = {1, 2, 3, 4};
            string[] usernames = {"user1", "user2", "user3", "user4"};
            string[] emails = { "user1@example.com", "user2@example.com","user3@example.com","user4@example.com" };
            string[] passwords = { "pass1", "pass2", "pass3", "pass4" };
            // create counters for accepted and rejected rows
                    int rows = 0;
            int rejRows = 0;
            bool error = false;
            // Create the transaction
                    VerticaTransaction txn = _conn.BeginTransaction();
            // Create the parameterized query and assign parameter types
                    VerticaCommand command = _conn.CreateCommand();
            command.CommandText = "insert into TEST values (@id, @username, @email, @password)";
            command.Parameters.Add(new VerticaParameter("id", VerticaType.BigInt));
            command.Parameters.Add(new VerticaParameter("username", VerticaType.VarChar));
            command.Parameters.Add(new VerticaParameter("email", VerticaType.VarChar));
            command.Parameters.Add(new VerticaParameter("password", VerticaType.VarChar));
            // Prepare the statement
                    command.Prepare();

                    // Loop through the column arrays and insert the data
                    for (int i = 0; i < ids.Length; i++)            {
                command.Parameters["id"].Value = ids[i];
                command.Parameters["username"].Value = usernames[i];
                command.Parameters["email"].Value = emails[i];
                command.Parameters["password"].Value = passwords[i];
                try
                {
                    rows += command.ExecuteNonQuery();
                }
                catch (Exception e)
                {
                    Console.WriteLine("\nInsert failed - \n  " + e.Message + "\n");
                    ++rejRows;
                    error = true;
                }
            }
            if (error)
            {
                // Roll back if errors
                        Console.WriteLine("Errors. Rolling Back Transaction.");
                Console.WriteLine(rejRows + " rows rejected.");
                txn.Rollback();
            }
            else
            {
                // Commit if no errors
                        Console.WriteLine("No Errors. Committing Transaction.");
                txn.Commit();
                Console.WriteLine("Inserted " + rows + " rows. ");
            }
            _conn.Close();
        }
    }
}

2.3.2.4.3.3 - 通过 ADO.NET 进行流式数据传输

以下两个选项可用于通过 ADO.NET 将客户端上的文件中的数据以流式传输到 Vertica 数据库:

  • 使用 VerticaCopyStream ADO.NET 类以面向对象的方式进行流式数据传输

  • 执行 COPY LOCAL SQL 语句以进行流式数据传输

此部分中的主题介绍了使用这些选项的方法。

2.3.2.4.3.3.1 - 通过 VerticaCopyStream 从客户端进行流式传输

使用 VerticaCopyStream 类可以将数据从客户端系统流式传输到 Vertica 数据库。通过此类,您可以直接使用 SQL COPY 语句,而不必通过将 STDIN 替换为一个或多个数据流先将数据复制到数据库群集中的主机。

注意:

  • 使用事务并对 copy 命令禁用自动提交可提高性能。

  • 可以通过将 copy 命令与“no commit”修饰符结合使用来禁用自动提交。您必须显式禁用提交。使用 VerticaCopyStream 时,启用事务不会禁用自动提交。

  • 与 VerticaCopyStream 结合使用的 copy 命令使用 copy 语法。

  • 每次调用 execute 时,都会将 VerticaCopyStream.rejects 置零。如果要捕获拒绝数,请将 VerticaCopyStream.rejects 的值分配给另一个变量,然后再次调用 execute。

  • 可以使用 AddStream() 调用添加多个流。

示例用法:

以下示例演示了使用 VerticaCopyStream 将文件流复制到 Vertica。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            // Configure connection properties
                    VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
                builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";
            //open the connection
            VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();
            try
            {
                using (_conn)
                {
                    // Start a transaction
                            VerticaTransaction txn = _conn.BeginTransaction();

                            // Create a table for this example
                            VerticaCommand command = new VerticaCommand("DROP TABLE IF EXISTS copy_table", _conn);
                command.ExecuteNonQuery();
                    command.CommandText = "CREATE TABLE copy_table (Last_Name char(50), "
                                    + "First_Name char(50),Email char(50), "
                                    + "Phone_Number char(15))";
                    command.ExecuteNonQuery();
                    // Create a new filestream from the data file
                            string filename = "C:/customers.txt";
                 Console.WriteLine("\n\nLoading File: " + filename);
                    FileStream inputfile = File.OpenRead(filename);
                    // Define the copy command
                            string copy = "copy copy_table from stdin record terminator E'\n' delimiter '|'" + " enforcelength "
                        + " no commit";
                    // Create a new copy stream instance with the connection and copy statement
                            VerticaCopyStream vcs = new VerticaCopyStream(_conn, copy);

                            // Start the VerticaCopyStream process
                            vcs.Start();
                            // Add the file stream
                            vcs.AddStream(inputfile, false);

                            // Execute the copy
                            vcs.Execute();

                            // Finish stream and write out the list of inserted and rejected rows
                            long rowsInserted = vcs.Finish();
                IList<long> rowsRejected = vcs.Rejects;
                // Does not work when rejected or exceptions defined
                    Console.WriteLine("Number of Rows inserted: " + rowsInserted);
                    Console.WriteLine("Number of Rows rejected: " + rowsRejected.Count);
                    if (rowsRejected.Count > 0)
                    {
                        for (int i = 0; i < rowsRejected.Count; i++)
                        {
                            Console.WriteLine("Rejected row #{0} is row {1}", i, rowsRejected[i]);
                        }
                    }

                            // Commit the changes
                            txn.Commit();
            }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }


                    //close the connection
                    _conn.Close();
    }
    }
}

2.3.2.4.3.3.2 - 将复制功能与 ADO.NET 配合使用

若要将 COPY 与 ADO.NET 结合使用,只需执行 COPY 语句并指定指向客户端系统上的源文件的路径即可。此方法比使用 VerticaCopyStream 类更简单。但是,如果有许多文件要复制到数据库,或者如果数据来自本地文件以外的其他源(例如,通过网络连接进行流式传输),您可能倾向于使用 VerticaCopyStream。

以下示例代码演示了使用 COPY 将文件从客户端复制到数据库。此示例与“使用 COPY 语句进行批量加载”中所示的代码相同,并且数据文件的路径位于客户端系统而非服务器上。

若要加载存储在数据库节点上的数据,请使用 VerticaCommand 对象创建 COPY 命令:

  1. 通过数据文件存储在的节点创建与数据库的连接

  2. 使用连接创建命令对象。

    VerticaCommand command = _conn.CreateCommand();
    
  3. 复制数据。以下是使用 COPY 命令加载数据的示例。此示例通过使用 LOCAL 修饰符发出命令来复制位于客户端本地的文件。

    command.CommandText = "copy lcopy_table from '/home/dbadmin/customers.txt'"
      + " record terminator E'\n' delimiter '|'"
      + " enforcelength ";
    
    Int32 insertedRows = command.ExecuteNonQuery();
    Console.WriteLine(insertedRows + " inserted.");
    

示例用法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            // Configure connection properties
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
     builder.Host = "192.168.1.10";
        builder.Database = "VMart";
        builder.User = "dbadmin";

                   // Open the connection
                    VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();
            try
            {
                using (_conn)
                {

                            // Start a transaction
                            VerticaTransaction txn = _conn.BeginTransaction();

                            // Create a table for this example
                            VerticaCommand command = new VerticaCommand("DROP TABLE IF EXISTS lcopy_table", _conn);
                    command.ExecuteNonQuery();
                    command.CommandText = "CREATE TABLE IF NOT EXISTS lcopy_table (Last_Name char(50), "
                                    + "First_Name char(50),Email char(50), "
                                    + "Phone_Number char(15))";
                    command.ExecuteNonQuery();
                    // Define the copy command
                            command.CommandText = "copy lcopy_table from '/home/dbadmin/customers.txt'"
            + " record terminator E'\n' delimiter '|'"
                        + " enforcelength "
                + " no commit";
                            // Execute the copy
        Int32 insertedRows = command.ExecuteNonQuery();
Console.WriteLine(insertedRows + " inserted.");
                            // Commit the changes
                            txn.Commit();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e.Message);
            }


                    // Close the connection
                    _conn.Close();
        }
    }
}

2.3.2.5 - 取消 ADO.NET 查询

您可以通过调用任何 Command 对象的 .Cancel() 方法来取消正在运行的 vsql 查询。SampleCancelTests 类演示了如何在读取指定数量的行后取消查询。它实现了以下方法:

  • SampleCancelTest() 执行 Setup() 函数以创建测试表。然后,它调用 RunQueryAndCancel()RunSecondQuery() 以演示如何在读取指定的行数后取消查询。最后,它运行 Cleanup() 函数以删除测试表。
  • Setup() 为示例查询创建一个数据库。
  • Cleanup() 删除数据库。
  • RunQueryAndCancel() 从返回 100 多个行的查询中准确读取 100 行。
  • RunSecondQuery() 从查询中读取所有行。
using System;
using Vertica.Data.VerticaClient;

class SampleCancelTests
{
    // Creates a database table, executes a query that cancels during a read loop,
    // executes a query that does not cancel, then drops the test database table.
    // connection: A connection to a Vertica database.

    public static void SampleCancelTest(VerticaConnection connection)
    {
        VerticaCommand command = connection.CreateCommand();

        Setup(command);

        try
        {
            Console.WriteLine("Running query that will cancel after reading 100 rows...");
            RunQueryAndCancel(command);
            Console.WriteLine("Running a second query...");
            RunSecondQuery(command);
            Console.WriteLine("Finished!");
        }
        finally
        {
            Cleanup(command);
        }
    }

    // Set up the database table for the example.
    // command: A Command object used to execute the query.
    private static void Setup(VerticaCommand command)
    {
        // Create table used for test.
        Console.WriteLine("Creating and loading table...");
        command.CommandText = "DROP TABLE IF EXISTS adocanceltest";
        command.ExecuteNonQuery();
        command.CommandText = "CREATE TABLE adocanceltest(id INTEGER, time TIMESTAMP)";
        command.ExecuteNonQuery();
        command.CommandText = @"INSERT INTO adocanceltest
        SELECT row_number() OVER(), slice_time
            FROM(
                    SELECT slice_time FROM(
                    SELECT '2021-01-01'::timestamp s UNION ALL SELECT '2022-01-01'::timestamp s
                    ) sq TIMESERIES slice_time AS '1 second' OVER(ORDER BY s)
            ) sq2";
        command.ExecuteNonQuery();
    }

    // Clean up the database after running the example.
    // command: A Command object used to execute the query.
    private static void Cleanup(VerticaCommand command)
    {
        command.CommandText = "DROP TABLE IF EXISTS adocanceltest";
        command.ExecuteNonQuery();
    }

    // Execute a query that returns many rows and cancels after reading 100.
    // command: A Command object used to execute the query.
    private static void RunQueryAndCancel(VerticaCommand command)
    {
        command.CommandText = "SELECT COUNT(id) from adocanceltest";
        int fullRowCount = Convert.ToInt32(command.ExecuteScalar());

        command.CommandText = "SELECT id, time FROM adocanceltest";
        VerticaDataReader dr = command.ExecuteReader();
        int nCount = 0;
        try
        {
            while (dr.Read())
            {
                nCount++;
                if (nCount == 100)
                {
                    // After reaching 100 rows, cancel the command
                    // Note that it is not necessary to read the remaining rows
                    command.Cancel();
                    return;
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        finally
        {
            dr.Close();
            // Verify that the cancel stopped the query
            Console.WriteLine((fullRowCount - nCount) + " rows out of " + fullRowCount + " discarded by cancel");
        }
    }

    // Execute a simple query and read all results.
    // command: A Command object used to execute the query.
    private static void RunSecondQuery(VerticaCommand command)
    {
        command.CommandText = "SELECT 1 FROM dual";
        VerticaDataReader dr = command.ExecuteReader();
        try
        {
            while (dr.Read())
            {
                ;
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            Console.WriteLine("Warning: no exception should be thrown on query after cancel");
        }
        finally
        {
            dr.Close();
        }
    }
}

2.3.2.6 - 处理消息

可以通过对 VerticaConnection 委托类使用 InfoMessage 事件来捕获 Vertica 向 ADO.NET 驱动程序提供的信息消息和警告消息。此类可捕获严重性不足以强制触发异常但仍可能提供对应用程序有益的信息的消息。

要使用 VerticaInfoMessageEventHander 类:

  1. 创建一个方法以处理从事件处理程序发送的消息:

    static void conn_InfoMessage(object sender, VerticaInfoMessageEventArgs e)
    {
        Console.WriteLine(e.SqlState + ": " + e.Message);
    }
    
  2. 为 InfoMessage 事件创建一个连接并注册新的 VerticaInfoMessageHandler 委托:

    _conn.InfoMessage += new VerticaInfoMessageEventHandler(conn_InfoMessage);
    
  3. 执行查询。如果生成了消息,则会运行事件处理函数。

  4. 可以使用以下命令取消订阅事件:

    _conn.InfoMessage -= new VerticaInfoMessageEventHandler(conn_InfoMessage);
    

示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication {
  class Program {
    // define message handler to deal with messages
    static void conn_InfoMessage(object sender, VerticaInfoMessageEventArgs e) {
      Console.WriteLine(e.SqlState + ": " + e.Message);
    }
    static void Main(string[] args) {
      //configure connection properties
      VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
      builder.Host = "192.168.1.10";
      builder.Database = "VMart";
      builder.User = "dbadmin";

      //open the connection
      VerticaConnection _conn = new VerticaConnection(builder.ToString());
      _conn.Open();

      //create message handler instance by subscribing it to the InfoMessage event of the connection
      _conn.InfoMessage += new VerticaInfoMessageEventHandler(conn_InfoMessage);

      //create and execute the command
      VerticaCommand cmd = _conn.CreateCommand();
      cmd.CommandText = "drop table if exists fakeTable";
      cmd.ExecuteNonQuery();

      //close the connection
      _conn.Close();
    }
  }
}

运行该示例后,将显示以下内容:

00000: Nothing was dropped

2.3.2.7 - 获取表元数据

可以通过对连接使用 GetSchema() 方法并将元数据加载到 DataTable 中来获取表元数据:

DataTable table = _conn.GetSchema("Tables", new string[] { database_name, schema_name, table_name, table_type });

例如:

DataTable table = _conn.GetSchema("Tables", new string[] { null, null, null, "SYSTEM TABLE" });

database_nameschema_nametable_name 可以设置为 null,也可以设置为特定名称,或者也可以使用 LIKE 模式。

table_type 可以设置为以下值之一:

  • "SYSTEM TABLE"

  • "TABLE"

  • "GLOBAL TEMPORARY"

  • "LOCAL TEMPORARY"

  • "VIEW"

  • NULL

如果 table_type 设置为 null,则会返回所有元数据表的元数据。

示例用法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            //configure connection properties
            VerticaConnectionStringBuilder builder = new VerticaConnectionStringBuilder();
            builder.Host = "192.168.1.10";
            builder.Database = "VMart";
            builder.User = "dbadmin";

            //open the connection
            VerticaConnection _conn = new VerticaConnection(builder.ToString());
            _conn.Open();

            //create a new data table containing the schema
            //the last argument can be "SYSTEM TABLE", "TABLE", "GLOBAL TEMPORARY",
            // "LOCAL TEMPORARY", "VIEW", or null for all types
            DataTable table = _conn.GetSchema("Tables", new string[] { null, null, null, "SYSTEM TABLE" });

            //print out the schema
            foreach (DataRow row in table.Rows) {
                foreach (DataColumn col in table.Columns)
                {
                    Console.WriteLine("{0} = {1}", col.ColumnName, row[col]);
                }
                Console.WriteLine("============================");
            }

            //close the connection
            _conn.Close();
        }
    }
}

2.3.3 - Java

Vertica JDBC 驱动程序为您提供了标准 JDBC API。如果已使用 JDBC 访问过其他数据库,您应该会觉得访问 Vertica 的方式有些熟悉。此部分介绍如何使用 JDBC 将 Java 应用程序连接到 Vertica。

先决条件

在创建 Java 客户端应用程序之前,您必须安装 JDBC 客户端驱动程序

2.3.3.1 - JDBC 功能支持

Vertica JDBC 驱动程序符合 JDBC 4.0 标准(但它不实施这些标准中的所有可选功能)。应用程序可以使用 DatabaseMetaData 类来确定驱动程序是否支持它要使用的特定功能。此外,此驱动程序实施 Wrapper 接口,该接口可让客户端代码发现特定于 Vertica 的 JDBC 标准类(例如,VerticaConnection 类和 VerticaStatement 类)扩展。

使用 Vertica JDBC 驱动程序时,应谨记以下一些重要事实:

  • 光标仅能向前移动,而不可滚动。无法更新结果集。

  • 一个连接在任何时候都仅支持执行单个语句。如果要同时执行多个语句,您必须打开多个连接。

  • 从客户端驱动程序 12.0.0 版开始,支持 CallableStatement

多个 SQL 语句支持

Vertica JDBC 驱动程序可以执行包含多个语句的字符串。例如:

stmt.executeUpdate("CREATE TABLE t(a INT);INSERT INTO t VALUES(10);");

Statement 界面支持执行包含多个 SQL 语句的字符串。不能将多个语句字符串与 PreparedStatement 一起使用。从主机文件系统复制文件的 COPY 语句可使用多个语句字符串。但是,客户端 COPY 语句 (COPY FROM STDIN) 不起作用。

将多批内容转换为 COPY 语句

Vertica JDBC 驱动程序可将所有批量插入转换为 Vertica COPY 语句。如果关闭 JDBC 连接的 AutoCommit 属性,JDBC 驱动程序将使用单个 COPY 语句加载连续批量插入中的数据,这样可通过减少开销来提高加载性能。有关详细信息,请参阅使用 JDBC 预定义的语句批量插入

多个 JDBC 版本支持

Vertica JDBC 驱动程序同时实施符合 JDBC 3.0 标准和 JDBC 4.0 标准的接口。驱动程序返回到应用程序的接口取决于应用程序运行于的 JVM 版本。如果应用程序正在 5.0 JVM 上运行,则驱动程序会向应用程序提供 JDBC 3.0 类。如果应用程序正在 6.0 或更高版本 JVM 上运行,则驱动程序会向应用程序提供 JDBC 4.0 类。

多重活动结果集 (MARS)

Vertica JDBC 驱动程序支持多重活动结果集 (MARS)。MARS 允许在单个连接中执行多个查询。ResultBufferSize 将查询的结果直接发送到客户端,而 MARS 先将结果存储在服务器上。完成查询和存储所有结果之后,您可向服务器发送检索请求以将行返回到客户端。

2.3.3.2 - 创建和配置连接

Java 应用程序必须先创建连接才能与 Vertica 交互。使用 JDBC 连接到 Vertica 与连接到大多数其他数据库相似。

导入 SQL 包

创建连接之前,您必须导入 Java SQL 包。执行此操作的一种简单方法是使用通配符导入整个包:

import java.sql.*;

您可能还需要导入 Properties 类。在将连接实例化时,您可以使用此类的实例来传递连接属性,而不必将所有内容都编码到连接字符串中:

import java.util.Properties;

如果应用程序需要在 Java 5 JVM 中运行,它将使用符合 JDBC 3.0 的较旧版本驱动程序。此驱动程序要求使用 Class.forName() 方法手动加载 Vertica JDBC 驱动程序:

// Only required for old JDBC 3.0 driver
try {
    Class.forName("com.vertica.jdbc.Driver");
} catch (ClassNotFoundException e) {
    // Could not find the driver class. Likely an issue
    // with finding the .jar file.
    System.err.println("Could not find the JDBC driver class.");
    e.printStackTrace();
    return; // Exit. Cannot do anything further.
}

应用程序可能在 Java 6 或更高版本的 JVM 中运行。如果是这样,则 JVM 会自动加载兼容 Vertica JDBC 4.0 的驱动程序,而不要求调用 Class.forName。但是,发出此调用不会对进程产生负面影响。因此,如果您希望应用程序同时与 Java 5 和 Java 6(或更高版本)JVM 兼容,应用程序仍可以调用 Class.forName

打开连接

导入 SQL 包之后,您便已准备好通过调用 DriverManager.getConnection() 方法来创建连接。您至少必须向此方法提供以下信息:

  • 数据库群集中的节点的 IP 地址或主机名。

    您可以提供 IPv4 地址、IPv6 地址或主机名。

    在 IPv4/IPv6 混合网络中,DNS 服务器配置决定了哪个 IP 版本地址最先发送。可使用 PreferredAddressFamily 选项来强制连接使用 IPv4 或 IPv6。

  • 数据库的端口号

  • 数据库的名称

  • 数据库用户帐户的用户名

  • 用户的密码(如果该用户具有密码)

前三个参数始终作为连接字符串的一部分提供,连接字符串是一个 URL,可指示 JDBC 驱动程序从何处查找数据库。连接字符串的格式为:

"jdbc:vertica://VerticaHost:portNumber/databaseName"

此连接字符串的第一部分选择了 Vertica JDBC 驱动程序,后跟数据库的位置。

可以通过以下三种方法之一向 JDBC 驱动程序提供后两个参数(用户名和密码):

  • 作为连接字符串的一部分。将以 URL 参数的相似方式对这两个参数进行编码:

    "jdbc:vertica://VerticaHost:portNumber/databaseName?user=username&password=password"
    
  • 作为单独的参数传递到 DriverManager.getConnection()

    Connection conn = DriverManager.getConnection(
            "jdbc:vertica://VerticaHost:portNumber/databaseName",
            "username", "password");
    
  • Properties 对象中:

    Properties myProp = new Properties();
    myProp.put("user", "username");
    myProp.put("password", "password");
    Connection conn = DriverManager.getConnection(
        "jdbc:vertica://VerticaHost:portNumber/databaseName", myProp);
    

对以上三种方法来说,Properties 对象是最灵活的,因为使用此对象可以轻松地将其他连接属性传递到 getConnection() 方法。有关其他连接属性的详细信息,请参阅连接属性设置和获取连接属性值

如果建立与数据库的连接时出现任何问题,getConnection() 方法将在其子类之一上引发 SQLException。若要防止异常,请将方法包含在 try-catch 块中,如以下有关建立连接的完整示例所示。

import java.sql.*;
import java.util.Properties;

public class VerySimpleVerticaJDBCExample {
    public static void main(String[] args) {
        /*
         * If your client needs to run under a Java 5 JVM, It will use the older
         * JDBC 3.0-compliant driver, which requires you manually load the
         * driver using Class.forname
         */
        /*
         * try { Class.forName("com.vertica.jdbc.Driver"); } catch
         * (ClassNotFoundException e) { // Could not find the driver class.
         * Likely an issue // with finding the .jar file.
         * System.err.println("Could not find the JDBC driver class.");
         * e.printStackTrace(); return; // Bail out. We cannot do anything
         * further. }
         */
        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "vertica");
        myProp.put("loginTimeout", "35");
        myProp.put("KeystorePath", "c:/keystore/keystore.jks");
     myProp.put("KeystorePassword", "keypwd");
     myProp.put("TrustStorePath", "c:/truststore/localstore.jks");
     myProp.put("TrustStorePassword", "trustpwd");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                    "jdbc:vertica://V_vmart_node0001.example.com:5433/vmart", myProp);
            System.out.println("Connected!");
            conn.close();
        } catch (SQLTransientConnectionException connException) {
            // There was a potentially temporary network error
            // Could automatically retry a number of times here, but
            // instead just report error and exit.
            System.out.print("Network connection issue: ");
            System.out.print(connException.getMessage());
            System.out.println(" Try again later!");
            return;
        } catch (SQLInvalidAuthorizationSpecException authException) {
            // Either the username or password was wrong
            System.out.print("Could not log into database: ");
            System.out.print(authException.getMessage());
            System.out.println(" Check the login credentials and try again.");
            return;
        } catch (SQLException e) {
            // Catch-all for other exceptions
            e.printStackTrace();
        }
    }
}

使用密钥库和信任库创建连接

您可以使用密钥库和信任库创建与 JDBC 客户端驱动程序的安全连接。有关 Vertica 中的安全性的详细信息,请参阅安全性和身份验证

有关如何在 Vertica 中生成(或导入外部)证书的示例和说明,请参阅生成 TLS 证书和密钥

要在 Vertica 中查看您的密钥和证书,请参阅CERTIFICATESCRYPTOGRAPHIC_KEYS

  1. 生成您自己的自签名证书或使用现有 CA(证书颁发机构)证书作为根 CA。有关此过程的信息,请参考 Schannel 文档

  2. 可选:生成或导入由根 CA 签署的中间 CA 证书。虽然中间 CA 证书不是必需的,但拥有中间 CA 证书对于测试和调试连接很有用。

  3. 为 Vertica 生成并签署(或导入)服务器证书。

  4. 使用 ALTER TLS CONFIGURATION 将 Vertica 配置为使用客户端/服务器 TLS 进行新连接。有关详细信息,请参阅配置客户端-服务器 TLS

    对于服务器模式(无客户端证书验证):

    => ALTER TLS CONFIGURATION server TLSMODE 'ENABLE';
    => ALTER TLS CONFIGURATION server CERTIFICATE server_cert;
    

    对于相互模式(根据 TLSMODE 进行不同严格性的客户端证书验证):

    => ALTER TLS CONFIGURATION server TLSMODE 'TRY_VERIFY';
    => ALTER TLS CONFIGURATION server CERTIFICATE server_cert ADD CA CERTIFICATES ca_cert;
    
  5. 或者,您可以使用 CREATE AUTHENTICATION 禁用所有非 SSL 连接。

    => CREATE AUTHENTICATION no_tls METHOD 'reject' HOST NO TLS '0.0.0.0/0';
    => CREATE AUTHENTICATION no_tls METHOD 'reject' HOST NO TLS '::/128';
    
  6. 使用签署服务器证书的同一 CA 为您的客户端生成和签署证书。

  7. 将 pem 证书链转换为单个 pkcs 12 文件。

  8. 将客户端密钥和链从 pkcs12 文件导入到密钥库 JKS 文件中。有关使用 keytool 命令界面的信息,请参考 Java 文档

    $ keytool -importkeystore -srckeystore -alias my_alias -srcstoretype PKCS12 -srcstorepass my_password -noprompt -deststorepass my_password -destkeypass my_password -destkeystore /tmp/keystore.jks
    
  9. 将 CA 导入到信任库 JKS 文件中。

    $ keytool -import -file certs/intermediate_ca.pem -alias my_alias -trustcacerts -keystore /tmp/truststore.jks -storepass my_truststore_password -noprompt
    

用法注意事项

  • 断开用户会话的连接时,任何未提交的事务会自动回退。

  • 如果数据库不符合 Vertica 许可条款,则 Vertica 会在您建立与数据库的连接时发出 SQLWarning。您可以使用 Connection.getWarnings() 方法检索此警告。有关遵守许可条款的详细信息,请参阅管理许可证

2.3.3.2.1 - JDBC 连接属性

可以使用连接属性来配置 JDBC 客户端应用程序和 Vertica 数据库之间的连接。属性提供有关连接的基本信息,例如,用来连接到数据库的服务器名称和端口号。您还可以使用属性来调整连接的性能以及启用日志记录。

可以采用以下方式之一设置连接属性:

  • 将属性名称和值包括在传递给方法 DriverManager.getConnection() 的连接字符串中。

  • Properties 对象中设置属性,然后将其传递给方法 DriverManager.getConnection()

  • 使用方法 VerticaConnection.setProperty()。使用此方法,您只能更改那些在创建连接后仍可更改的连接属性。

此外,一些标准 JDBC 连接属性在 Connection 界面上具有 getter 和 setter,例如 Connection.setAutoCommit()

连接属性

您只能在打开与数据库的连接之前设置下表中的属性。其中两个属性对每个连接都是必需的。

OAuth 连接属性

以下连接属性与 JDBC 中的 OAuth 相关。

超时属性

使用以下参数,您可以为每个步骤指定各种超时以及 JDBC 与 Vertica 数据库的整体连接。

下图说明了这些属性之间的关系以及它们在 JDBC 尝试连接到 Vertica 数据库时所起的作用: 关于当 JDBC 尝试连接到一个主节点和两个备份节点时超时表现行为的图表

常规属性

可以在建立连接之后设置以下属性。这些属性全都不是必需的。

日志记录属性

必须在打开连接之前设置这些控制客户端日志记录的属性。这些属性全都不是必需的,您无法在 Connection 对象已实例化之后更改所有这些属性。

Kerberos 连接参数

可以使用以下参数为使用 Kerberos 的客户端身份验证设置服务名称主体和主机名主体。

可路由连接 API 连接参数

可以使用以下参数来设置属性,以便为连接启用和配置可路由连接查找。

有关操作这些属性的信息,请参阅设置和获取连接属性值

2.3.3.2.2 - 设置和获取连接属性值

可以采用以下方式之一设置连接属性:

  • 将属性名称和值包括在传递给方法 DriverManager.getConnection() 的连接字符串中。

  • Properties 对象中设置属性,然后将其传递给方法 DriverManager.getConnection()

  • 使用方法 VerticaConnection.setProperty()。使用此方法,您只能更改那些在创建连接后仍可更改的连接属性。

此外,一些标准 JDBC 连接属性在 Connection 界面上具有 getter 和 setter,例如 Connection.setAutoCommit()

在连接时设置属性

创建与 Vertica 的连接时,您可以通过以下方式设置连接属性:

  • 在连接字符串中指定这些属性。

  • 修改传递给 getConnection()Properties 对象。

连接字符串属性

您可以使用用于用户名和密码的相同 URL 参数格式在连接字符串中指定连接属性。例如,以下字符串启用 TLS 连接:

"jdbc:vertica://VerticaHost:5433/db?user=UserName&password=Password&TLSmode=require"

使用 setProperty() 方法设置主机名会替代已在连接字符串中设置的主机名。如果出现这种情况,Vertica 可能无法连接到主机。例如,如果使用以上连接字符串,以下代码会替代 VerticaHost 名称:

Properties props = new Properties();
props.setProperty("dataSource", dataSourceURL);
props.setProperty("database", database);
props.setProperty("user", user);
props.setProperty("password", password);
ps.setProperty("jdbcDriver", jdbcDriver);
props.setProperty("hostName", "NonVertica_host");

但是,如果需要新连接或替代连接,您可以在主机名属性对象中输入有效的主机名。

NonVertica_host 主机名将替代连接字符串中的 VerticaHost 名称。若要避免此问题,请将 props.setProperty("hostName", "NonVertica_host"); 行注释掉:

//props.setProperty("hostName", "NonVertica_host");

属性对象

要使用传递给 getConnection() 调用的 Properties 对象设置连接属性:

  1. 导入 java.util.Properties 类以实例化 Properties 对象。

  2. 使用 put() 方法向对象添加名称-值对。

Properties myProp = new Properties();
myProp.put("user", "ExampleUser");
myProp.put("password", "password123");
myProp.put("LoginTimeout", "35");
Connection conn;
try {
    conn = DriverManager.getConnection(
        "jdbc:vertica://VerticaHost:/ExampleDB", myProp);
} catch (SQLException e) {
    e.printStackTrace();
}

在连接后获取和设置属性

与 Vertica 建立连接后,您可以使用 VerticaConnection 方法 getProperty()setProperty() 分别设置一些连接属性的值。

使用 VerticaConnection.getProperty() 方法,您可以获取部分连接属性的值。使用此方法更改与 Vertica 建立连接后可设置的属性值。

由于这些方法特定于 Vertica,因此您必须使用以下方法之一将 Connection 对象强制转换为 VerticaConnection 接口:

  • Connection 对象导入客户端应用程序。

  • 使用完全限定的引用:com.vertica.jdbc.VerticaConnection

以下示例演示了获取和设置只读属性的值。

import java.sql.*;
import java.util.Properties;
import com.vertica.jdbc.*;

public class SetConnectionProperties {
    public static void main(String[] args) {
        // Note: If your application needs to run under Java 5, you need to
        // load the JDBC driver using Class.forName() here.
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");
        // Set ReadOnly to true initially
        myProp.put("ReadOnly", "true");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);
            // Show state of the ReadOnly property. This was set at the
            // time the connection was created.
            System.out.println("ReadOnly state: "
                            + ((VerticaConnection) conn).getProperty(
                                            "ReadOnly"));

            // Change it and show it again
            ((VerticaConnection) conn).setProperty("ReadOnly", false);
            System.out.println("ReadOnly state is now: " +
                             ((VerticaConnection) conn).getProperty(
                                             "ReadOnly"));
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行以上示例后,将在标准输出上输出以下内容:

ReadOnly state: true
ReadOnly state is now: false

2.3.3.2.3 - 为 JDBC 客户端配置 TLS

要为 JDBC 客户端配置 TLS:

设置密钥库/信任库属性

可以通过以下方式设置密钥库和信任库属性,每种方式各有利弊:

  • 在驱动程序级别。

  • 在 JVM 级别。

驱动程序级别配置

如果您将 DbVizualizer 等工具用于多个连接,请使用 JDBC 连接属性配置密钥库和信任库。但是,这确实会在连接字符串中公开这些值:

  • KeyStorePath

  • KeyStorePassword

  • TrustStorePath

  • TrustStorePassword

例如:

Properties props = new Properties();
props.setProperty("KeyStorePath", keystorepath);
props.setProperty("KeyStorePassword", keystorepassword);
props.setProperty("TrustStorePath", truststorepath);
props.setProperty("TrustStorePassword", truststorepassword);

JVM 级别配置

在 JVM 级别设置密钥库和信任库参数会将它们从连接字符串中排除,这可能更适合安全要求较严格的环境:

  • javax.net.ssl.keyStore

  • javax.net.ssl.trustStore

  • javax.net.ssl.keyStorePassword

  • javax.net.ssl.trustStorePassword

例如:

System.setProperty("javax.net.ssl.keyStore","clientKeyStore.key");
System.setProperty("javax.net.ssl.trustStore","clientTrustStore.key");
System.setProperty("javax.net.ssl.keyStorePassword","new_keystore_password")
System.setProperty("javax.net.ssl.trustStorePassword","new_truststore_password");

设置 TLSmode 连接属性

您可以设置 TLSmode 连接属性以确定如何处理证书。默认情况下,禁用 TLSmode。

TLSmode 标识 Vertica 应用于 JDBC 连接的安全级别。必须将 Vertica 配置为处理 TLS 连接,然后才能与其建立 TLS 加密连接。有关详细信息,请参阅TLS 协议。有效值包括:

  • disable:JDBC 使用纯文本连接并且不实施任何安全措施。

  • require:JDBC 使用 TLS 连接而不验证 CA 证书。

  • verify-ca:JDBC 使用 TLS 连接并确认服务器证书已由证书颁发机构签名。此设置等效于已弃用的 ssl=true 属性。

  • verify-full:JDBC 使用 TLS 连接,确认服务器证书已由证书颁发机构签名,并验证主机名与服务器证书中提供的名称匹配。

如果设置了此属性和 SSL 属性,则此属性优先。

例如,要将 JDBC 配置为使用 TLS 连接服务器而不验证 CA 证书,您可以通过 VerticaConnection.setProperty() 方法将 TLSmode 属性设置为 'require':

Properties props = new Properties();
props.setProperty("TLSmode", "verify-full");

运行 SSL 调试实用程序

配置 TLS 后,您可以为调试实用程序运行以下命令:

$ java -Djavax.net.debug=ssl

通过调试实用程序,可以使用多个调试指示符(选项)。指示符有助于缩小返回的调试信息的范围。例如,可以指定其中一个选项来打印握手消息或会话活动。

有关该调试实用程序及其选项的信息,请参阅 Oracle 文档《JSSE 参考指南》中的“调试实用程序”。

有关解释调试信息的说明,请参阅 Oracle 文档调试 SSL/TLS 连接

2.3.3.2.4 - 设置和返回客户端连接标记

JDBC 客户端具有用于设置和返回客户端连接标签的方法:getClientInfo() 和 setClientInfo()。这些方法可与 SQL 函数 GET_CLIENT_LABELSET_CLIENT_LABEL 配合使用。

使用这两种方法时,请确保向 setter 和 getter 方法都传递字符串值 APPLICATIONNAME

setClientInfo() 用于创建客户端标签,而 getClientInfo() 则用于返回客户端标签:

import java.sql.*;
import java.util.Properties;

public class ClientLabelJDBC {

    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "");
        myProp.put("loginTimeout", "35");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                    "jdbc:vertica://docc05.verticacorp.com:5433/doccdb", myProp);
            System.out.println("Connected!");
            conn.setClientInfo("APPLICATIONNAME", "JDBC Client - Data Load");
            System.out.println("New Conn label: " + conn.getClientInfo("APPLICATIONNAME"));
            conn.close();
        } catch (SQLTransientConnectionException connException) {
            // There was a potentially temporary network error
            // Could automatically retry a number of times here, but
            // instead just report error and exit.
            System.out.print("Network connection issue: ");
            System.out.print(connException.getMessage());
            System.out.println(" Try again later!");
            return;
        } catch (SQLInvalidAuthorizationSpecException authException) {
            // Either the username or password was wrong
            System.out.print("Could not log into database: ");
            System.out.print(authException.getMessage());
            System.out.println(" Check the login credentials and try again.");
            return;
        } catch (SQLException e) {
            // Catch-all for other exceptions
            e.printStackTrace();
        }
    }
}

运行此方法时,会将以下结果打印到标准输出:

Connected!
New Conn Label: JDBC Client - Data Load

2.3.3.2.5 - 设置 JDBC 会话的区域设置

可以通过以下方法设置连接的区域设置:在打开该连接时将 SET LOCALE 语句包含到 ConnSettings 属性;或者在打开该连接之后的任何时候执行 SET LOCALE 语句。更改 Connection 对象的区域设置会影响使用该对象实例化的所有 Statement 对象。

可以通过执行 SHOW LOCALE 查询来获取区域设置。以下示例演示了使用 ConnSettings 设置区域设置以及执行语句和获取区域设置:

import java.sql.*;
import java.util.Properties;

public class GetAndSetLocale {
    public static void main(String[] args) {

     // If running under a Java 5 JVM, you need to load the JDBC driver
     // using Class.forname here

     Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");

        // Set Locale to true en_GB on connection. After the connection
        // is established, the JDBC driver runs the statements in the
        // ConnSettings property.
        myProp.put("ConnSettings", "SET LOCALE TO en_GB");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);

            // Execute a query to get the locale. The results should
            // show "en_GB" as the locale, since it was set by the
            // conn settings property.
            Statement stmt = conn.createStatement();
            ResultSet rs = null;
            rs = stmt.executeQuery("SHOW LOCALE");
            System.out.print("Query reports that Locale is set to: ");
            while (rs.next()) {
                System.out.println(rs.getString(2).trim());
            }

            // Now execute a query to set locale.
            stmt.execute("SET LOCALE TO en_US");

            // Run query again to get locale.
            rs = stmt.executeQuery("SHOW LOCALE");
            System.out.print("Query now reports that Locale is set to: ");
            while (rs.next()) {
                System.out.println(rs.getString(2).trim());
            }
            // Clean up
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行以上示例后,将在系统控制台上显示以下内容:

Query reports that Locale is set to: en_GB (LEN)
Query now reports that Locale is set to: en_US (LEN)

注意:

  • JDBC 应用程序使用 UTF-16 字符集编码,并负责将任何非 UTF-16 编码数据转换为 UTF-16。未能转换数据会导致发生错误或不正确地存储数据。

  • 向 Vertica 服务器传递数据时,JDBC 驱动程序会将 UTF-16 数据转换为 UTF-8,并且会将 Vertica 服务器发送的数据从 UTF-8 转换为 UTF-16。

2.3.3.2.6 - 更改事务隔离级别

通过更改事务隔离级别,您可以选择让事务以哪种方式防止来自其他事务的干扰。默认情况下,JDBC 驱动程序会匹配 Vertica 服务器的事务隔离级别。Vertica 的默认事务隔离级别是 READ_COMMITTED,这意味着在提交由某个事务进行的任何更改之前,任何其他事务无法读取这些更改。此级别可防止事务读取由稍后回退的其他事务插入的数据。

Vertica 还支持 SERIALIZABLE 事务隔离级别。此级别可锁定表,以防止其他事务更改查询的 WHERE 子句的结果。锁定表会产生性能影响,因为一次只有一个事务能够访问该表。

事务将保留其隔离级别直至其完成,即使在事务处理期间会话的隔离级别发生更改也是如此。Vertica 内部进程(例如 Tuple Mover刷新操作)以及 DDL 操作始终以 SERIALIZABLE 隔离级别运行以确保一致性。

建立了连接之后,您可以使用 Connection 对象的 setter (setTransactionIsolation()) 和 getter (getTransactionIsolation()) 更改事务隔离级别的连接属性。事务隔离属性的值是一个整数。Connection 接口定义了一些常数,这些常数有助于以更直观的方式设置值:

以下示例演示了将事务隔离级别设置为 SERIALIZABLE。

import java.sql.*;
import java.util.Properties;

public class SetTransactionIsolation {
    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);
            // Get default transaction isolation
            System.out.println("Transaction Isolation Level: "
                            + conn.getTransactionIsolation());
            // Set transaction isolation to SERIALIZABLE
            conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
            // Get the transaction isolation again
            System.out.println("Transaction Isolation Level: "
                            + conn.getTransactionIsolation());
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行此示例后,以下内容会输出到控制台:

Transaction Isolation Level: 2Transaction Isolation Level: 8

2.3.3.2.7 - JDBC 连接池

池数据源一组持续连接来减少在客户端和服务器之间重复打开网络连接的开销。与持续打开一个小型连接池并使其可供新请求使用相比,为每个请求打开新连接对服务器和客户端来说成本更高。当请求到达时,将为其分配该池中预先存在的连接之一。仅当该池中没有可用连接时,才会创建新连接。该请求完成后,连接将返回到池中并等待为其他请求提供服务。

Vertica JDBC 驱动程序支持 JDBC 4.0 标准中定义的连接池。如果要将基于 J2EE 的应用程序服务器与 Vertica 结合使用,该应用程序服务器应已有内置的数据池功能。唯一的要求是该应用程序服务器可与 Vertica 的 JDBC 驱动程序所实施的 PooledConnection 接口配合工作。应用程序服务器的池功能通常已针对该服务器设计用于处理的工作负载进行了适当优化。有关如何使用池连接的详细信息,请参阅应用程序服务器的文档。一般情况下,应在代码中以透明方式使用池连接,您只需打开连接,应用程序将处理池连接的详细信息。

如果不想使用应用程序服务器,或者应用程序服务器未提供与 Vertica 兼容的连接池,您可以使用第三方池库(例如,开源 c3p0 库或 DBCP 库)来实施连接池。

2.3.3.2.8 - JDBC 中的负载均衡

本机连接负载均衡

本机连接负载均衡有助于在 Vertica 数据库中的主机上分散客户端连接所带来的开销。服务器和客户端都必须启用本机连接负载均衡。如果两者者都启用了本机负载均衡,则当客户端最初连接到数据库中的主机时,此主机会从数据库中当前正在运行的主机列表中选择一个主机来处理客户端连接,并通知客户端它所选择的主机。

如果最初联系的主机没有选择自身来处理连接,则客户端会断开连接,然后打开指向第一个主机所选主机的另一个连接。指向此第二个主机的连接进程将照常进行—如果启用了 SSL,则会启动 SSL 协商,否则客户端会启动身份验证过程。有关详细信息,请参阅关于本机连接负载均衡

若要在客户端上启用本机连接负载均衡,请将 ConnectionLoadBalance 连接参数设置为 true。以下示例演示了下列操作:

  • 在启用本机连接负载均衡的情况下多次连接到数据库。

  • 从 V_MONITOR.CURRENT_SESSION 系统表中获取处理连接的节点的名称。


import java.sql.*;
import java.util.Properties;
import java.sql.*;
import java.util.Properties;

public class JDBCLoadingBalanceExample {
    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "example_password123");
        myProp.put("loginTimeout", "35");
        myProp.put("ConnectionLoadBalance", "1");
        Connection conn;

        for (int x = 1; x <= 4; x++) {
            try {
                System.out.print("Connect attempt #" + x + "...");
                conn = DriverManager.getConnection(
                    "jdbc:vertica://node01.example.com:5433/vmart", myProp);
                Statement stmt = conn.createStatement();
                // Set the load balance policy to round robin before testing the database's load balancing.
                stmt.execute("SELECT SET_LOAD_BALANCE_POLICY('ROUNDROBIN');");
                // Query system to table to see what node we are connected to. Assume a single row
                // in response set.
                ResultSet rs = stmt.executeQuery("SELECT node_name FROM v_monitor.current_session;");
                rs.next();
                System.out.println("Connected to node " + rs.getString(1).trim());
                conn.close();
            } catch (SQLTransientConnectionException connException) {
                // There was a potentially temporary network error
                // Could automatically retry a number of times here, but
                // instead just report error and exit.
                System.out.print("Network connection issue: ");
                System.out.print(connException.getMessage());
                System.out.println(" Try again later!");
                return;
            } catch (SQLInvalidAuthorizationSpecException authException) {
                // Either the username or password was wrong
                System.out.print("Could not log into database: ");
                System.out.print(authException.getMessage());
                System.out.println(" Check the login credentials and try again.");
                return;
            } catch (SQLException e) {
                // Catch-all for other exceptions
                e.printStackTrace();
            }
        }
    }
}

运行上述示例得到了以下输出:


Connect attempt #1...Connected to node v_vmart_node0002
Connect attempt #2...Connected to node v_vmart_node0003
Connect attempt #3...Connected to node v_vmart_node0001
Connect attempt #4...Connected to node v_vmart_node0002

基于主机名的负载均衡

您还可以通过将单个主机名解析为多个 IP 地址来对工作负载进行负载均衡。当您为 DriverManager.getConnection() 方法指定主机名时,主机名会解析为来自每个连接的随机列出的 IP 地址。

例如,主机名 verticahost.example.cometc/hosts 中有以下条目:

192.0.2.0 verticahost.example.com
192.0.2.1 verticahost.example.com
192.0.2.2 verticahost.example.com

verticahost.example.com 指定为 DriverManager.getConnection() 的连接会随机解析为列出的 IP 地址之一。

2.3.3.2.9 - JDBC 连接故障转移

如果客户端应用程序尝试连接到 Vertica 群集中处于关闭状态的主机,则使用默认连接配置时连接尝试将会失败。此故障通常会向用户返回一个错误。用户必须等待主机恢复并重试连接,或者手动编辑连接设置以选择其他主机。

由于 Vertica 分析型数据库采用分布式架构,通常您不会关注哪个数据库主机处理客户端应用程序的连接。可以使用客户端驱动程序的连接故障转移功能来防止用户在无法访问连接设置中所指定主机的情况下收到连接错误。JDBC 驱动程序可通过几种方式让客户端驱动程序在无法访问连接参数中所指定的主机时自动尝试连接到其他主机:

  • 将 DNS 服务器配置为返回一个主机名的多个 IP 地址。在连接设置中使用此主机名时,客户端会尝试连接到 DNS 找到的第一个 IP 地址。如果位于该 IP 地址的主机无法访问,则客户端会尝试连接到第二个 IP,依此类推,直到其成功地连接到某个主机或试过所有 IP 地址为止。

  • 提供当您在连接参数中指定的主要主机无法访问时客户端驱动程序尝试连接的备份主机列表。

  • (仅限 JDBC)在尝试连接到下一个节点之前,使用特定于驱动程序的连接属性来管理超时。

在所有方法中,故障转移过程对于客户端应用程序是透明的(除了在选择使用列表故障转移方法时需指定备份主机列表之外)。如果主要主机无法访问,客户端驱动程序会自动尝试连接到其他主机。

故障转移仅适用于初次建立客户端连接的情况。如果连接断开,驱动程序不会自动尝试重新连接到数据库中的其他主机。

通常可选择上述两种故障转移方法中的一种。但它们可以一起使用。如果您的 DNS 服务器返回多个 IP 地址,并且您提供了备份主机列表,则客户端会首先尝试连接 DNS 服务器返回的所有 IP,然后再尝试连接备份列表中的主机。

DNS 故障转移方法可集中处理配置客户端故障转移。在向 Vertica 分析型数据库群集添加新节点时,可以选择通过编辑 DNS 服务器设置来将这些节点添加到故障转移列表中。所有使用 DNS 服务器连接到 Vertica 分析型数据库的客户端系统都会自动采用连接故障转移,而无需更改任何设置。但此方法需要对所有客户端用于连接到 Vertica 分析型数据库群集的 DNS 服务器具有管理访问权限。这在贵组织中可能无法实现。

使用备份服务器列表要比编辑 DNS 服务器设置更加容易。但此方法不能集中处理故障转移功能。如果更改了 Vertica 分析型数据库群集,您可能需要在每个客户端系统上更新应用程序设置。

使用 DNS 故障转移

若要使用 DNS 故障转移,您需要更改 DNS 服务器的设置,将单一主机名映射为 Vertica 分析型数据库群集中主机的多个 IP 地址。然后让所有客户端应用程序都使用该主机名连接到 Vertica 分析型数据库。

您可以选择让 DNS 服务器为主机名返回任何所需数量的 IP 地址。在小型群集中,您可以选择让其返回群集中所有主机的 IP 地址。但对于大型群集,应考虑选择返回一部分主机。否则可能会导致长时间延迟,因为客户端驱动程序尝试连接到数据库中处于关闭状态的每个主机会失败。

使用备份主机列表

若要启用基于备份列表的连接故障转移,需要在客户端应用程序的 BackupServerNode 参数中指定主机的至少一个 IP 地址或主机名。可以视情况在主机名或 IP 后面使用冒号和端口号。如果未提供端口号,驱动程序会默认使用标准 Vertica 端口号 (5433)。若要列出多个主机,请用逗号分隔这些主机。

以下示例演示了如何通过设置 BackupServerNode 连接参数来指定可尝试连接的其他主机。由于有意在连接字符串中使用了一个不存在的节点,因此初始连接失败。客户端驱动程序必须尝试通过备份主机来与 Vertica 建立连接。

import java.sql.*;
import java.util.Properties;

public class ConnectionFailoverExample {
    public static void main(String[] args) {
        // Assume using JDBC 4.0 driver on JVM 6+. No driver loading needed.
        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "vertica");
        // Set two backup hosts to be used if connecting to the first host
        // fails. All of these hosts will be tried in order until the connection
        // succeeds or all of the connections fail.
        myProp.put("BackupServerNode", "VerticaHost02,VerticaHost03");
        Connection conn;
        try {
            // The connection string is set to try to connect to a known
            // bnad host (in this case, a host that never existed).
            conn = DriverManager.getConnection(
                    "jdbc:vertica://BadVerticaHost:5433/vmart", myProp);
            System.out.println("Connected!");
            // Query system to table to see what node we are connected to.
            // Assume a single row in response set.
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(
                    "SELECT node_name FROM v_monitor.current_session;");
            rs.next();
            System.out.println("Connected to node " + rs.getString(1).trim());
            // Done with connection.
            conn.close();
        } catch (SQLException e) {
            // Catch-all for other exceptions
            e.printStackTrace();
        }
    }
}

运行以上示例后,将在系统控制台上生成类似于以下内容的输出:

Connected!
Connected to node v_vmart_node0002

请注意,系统会与备份列表中的第一个节点建立连接(节点 2)。

指定连接超时

LoginTimeout 控制 JDBC 与节点建立 TCP 连接并登录 Vertica 的超时时间。

LoginNodeTimeout 控制 JDBC 登录 Vertica 数据库的超时时间。在指定的超时之后,JDBC 尝试连接到由连接属性 BackupServerNode 或 DNS 解析确定的“下一个”节点。这在节点已启动的情况下很有用,但 Vertica 进程会出现问题。

LoginNetworkTimeout 控制 JDBC 与 Vertica 节点建立 TCP 连接的超时时间。如果您未设置此连接属性,且 JDBC 客户端尝试连接的节点已关闭,则 JDBC 客户端将“无限期”等待,但实际上使用的是系统默认的 70 秒超时。LoginNetworkTimeout 的一个典型用例是,如果当前 Vertica 节点因维护而停机并且无法修改 JDBC 应用程序的连接字符串,则让 JDBC 连接到另一个节点。

NetworkTimeout 控制 Vertica 在建立连接并登录到数据库后响应客户端请求的超时时间。

要在连接字符串中设置这些参数:

# LoginTimeout is 30 seconds, LoginNodeTimeout is 10 seconds, LoginNetworkTimeout is 2 seconds, NetworkTimeout is 0.5 seconds
Connection conn = DriverManager.getConnection("jdbc:vertica://VerticaHost:5433/verticadb?user=dbadmin&loginTimeout=30&loginNodeTimeout=10"&loginNetworkTimeout=2&networkTimeout=500");

要将这些参数设置为连接属性:


Properties myProp = new Properties();
myProp.put("user", "dbadmin");
myProp.put("loginTimeout", "30"); // overall connection timeout is 30 seconds to make sure it is not too small for failover
myProp.put("loginNodeTimeout", "10"); // JDBC waits 10 seconds before attempting to connect to the next node if the Vertica process is running but does not respond
myProp.put("loginNetworkTimeout", "2"); // node connection timeout is 2 seconds
myProp.put("networkTimeout", "500"); // after the client has logged in, Vertica has 0.5 seconds to respond to each request
Connection conn = DriverManager.getConnection("jdbc:vertica://VerticaHost:5433/verticadb", myProp);

与负载均衡的交互

启用本机连接负载均衡时,在 BackupServerNode 连接参数中指定的其他服务器将只用于指向 Vertica 主机的初始连接。如果主机将客户端重定向至数据库群集中的其他主机来处理其连接请求,则第二个连接将不使用备份节点列表。这很少出现问题,因为本机连接负载均衡知道数据库中的哪个节点目前正处于运行状态。

有关详细信息,请参阅JDBC 中的负载均衡

2.3.3.3 - JDBC 数据类型

JDBC 驱动程序以透明方式将大部分 Vertica 数据类型转换为相应的 Java 数据类型。在少数情况下,Vertica 数据类型无法直接转换为 Java 数据类型;本节将介绍这些例外。

2.3.3.3.1 - VerticaTypes 类

JDBC 不支持 Vertica 所支持的所有数据类型。Vertica JDBC 客户端驱动程序包含一个名为VerticaTypes 的附加类,可帮助您处理识别这些特定于Vertica 的数据类型。它包含可在代码中用于指定 Vertica 数据类型的常量。此类定义了两种不同类别的数据类型:

  • Vertica 的 13 种时间间隔值。此类包含每种类型的常量属性。当实例化 VerticaDayTimeIntervalVerticaYearMonthInterval 类的成员时,您可以使用这些常量来选择特定的时间间隔类型:

    // Create a day to second interval.
    VerticaDayTimeInterval dayInt = new VerticaDayTimeInterval(
            VerticaTypes.INTERVAL_DAY_TO_SECOND, 10, 0, 5, 40, 0, 0, false);
    // Create a year to month interval.
    VerticaYearMonthInterval monthInt = new VerticaYearMonthInterval(
            VerticaTypes.INTERVAL_YEAR_TO_MONTH, 10, 6, false);
    
  • Vertica UUID 数据类型。使用 VerticaTypes.UUID 的一种方法是查询表的元数据以查看列是否为 UUID。有关示例,请参阅 UUID 值

有关此类的详细信息,请参阅 JDBC 文档。

2.3.3.3.2 - 数值数据别名转换

Vertica 服务器支持对整数、浮点和数字类型使用数据类型别名。JDBC 驱动程序会将这些数据类型报告为基本数据类型(BIGINT、DOUBLE PRECISION 和 NUMERIC),如下所示:

如果客户端应用程序将值检索为较小数据类型,Vertica JDBC 驱动程序不会检查溢出。以下示例演示了此溢出的结果。

import java.sql.*;
import java.util.Properties;

public class JDBCDataTypes {
    public static void main(String[] args) {
        // If running under a Java 5 JVM, use you need to load the JDBC driver
        // using Class.forname here

        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/VMart",
                             myProp);
            Statement statement = conn.createStatement();
            // Create a table that will hold a row of different types of
            // numeric data.
            statement.executeUpdate(
                    "DROP TABLE IF EXISTS test_all_types cascade");
            statement.executeUpdate("CREATE TABLE test_all_types ("
                            + "c0 INTEGER, c1 TINYINT, c2 DECIMAL, "
                            + "c3 MONEY, c4 DOUBLE PRECISION, c5 REAL)");
            // Add a row of values to it.
            statement.executeUpdate("INSERT INTO test_all_types VALUES("
                            + "111111111111, 444, 55555555555.5555, "
                            + "77777777.77,  88888888888888888.88, "
                            + "10101010.10101010101010)");
            // Query the new table to get the row back as a result set.
            ResultSet rs = statement
                            .executeQuery("SELECT * FROM test_all_types");
            // Get the metadata about the row, including its data type.
            ResultSetMetaData md = rs.getMetaData();
            // Loop should only run once...
            while (rs.next()) {
                // Print out the data type used to defined the column, followed
                // by the values retrieved using several different retrieval
                // methods.

                String[] vertTypes = new String[] {"INTEGER", "TINYINT",
                         "DECIMAL", "MONEY", "DOUBLE PRECISION", "REAL"};

                for (int x=1; x<7; x++) {
                    System.out.println("\n\nColumn " + x + " (" + vertTypes[x-1]
                            + ")");
                    System.out.println("\tgetColumnType()\t\t"
                            + md.getColumnType(x));
                    System.out.println("\tgetColumnTypeName()\t"
                            + md.getColumnTypeName(x));
                    System.out.println("\tgetShort()\t\t"
                            + rs.getShort(x));
                    System.out.println("\tgetLong()\t\t" + rs.getLong(x));
                    System.out.println("\tgetInt()\t\t" + rs.getInt(x));
                    System.out.println("\tgetByte()\t\t" + rs.getByte(x));
                }
            }
            rs.close();
            statement.executeUpdate("drop table test_all_types cascade");
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行以上示例后,将在控制台上输出以下内容:

Column 1 (INTEGER)
       getColumnType()        -5
    getColumnTypeName()    BIGINT
    getShort()        455
    getLong()        111111111111
    getInt()        -558038585
    getByte()        -57
Column 2 (TINYINT)
    getColumnType()        -5
    getColumnTypeName()    BIGINT
    getShort()        444
    getLong()        444
    getInt()        444
    getByte()        -68
Column 3 (DECIMAL)
    getColumnType()        2
    getColumnTypeName()    NUMERIC
    getShort()        -1
    getLong()        55555555555
    getInt()        2147483647
    getByte()        -1
Column 4 (MONEY)
    getColumnType()        2
    getColumnTypeName()    NUMERIC
    getShort()        -13455
    getLong()        77777777
    getInt()        77777777
    getByte()        113
Column 5 (DOUBLE PRECISION)
    getColumnType()        8
    getColumnTypeName()    DOUBLE PRECISION
    getShort()        -1
    getLong()        88888888888888900
    getInt()        2147483647
    getByte()        -1
Column 6 (REAL)
    getColumnType()        8
    getColumnTypeName()    DOUBLE PRECISION
    getShort()        8466
    getLong()        10101010
    getInt()        10101010
    getByte()        18

2.3.3.3.3 - 将时间间隔与 JDBC 配合使用

JDBC 标准不包含用于时间间隔(两个时间点之间的持续时间)的数据类型。若要处理 Vertica 的 INTERVAL 数据类型,您必须使用 JDBC 特定于数据库的对象类型。

从结果集中读取时间间隔值时,使用 ResultSet.getObject() 方法检索该值,然后将其强制转换为 Vertica 时间间隔类之一: VerticaDayTimeInterval (代表所有十种日期/时间的时间间隔)或 VerticaYearMonthInterval(代表所有三种年/月时间间隔)。

在批量插入中使用时间间隔

将多个批插入到包含时间间隔数据的表时,您必须创建 VerticaDayTimeIntervalVerticaYearMonthInterval 类的实例以用于存放要插入的数据。您可以在调用类的构造函数时设置值,或者也可以在使用 setter 之后设置值。然后,可以使用 PreparedStatement.setObject() 方法插入时间间隔值。您还可以使用 .setString() 方法,并向其传递采用 "DD ``HH:MM:SS""YY-MM" 格式的字符串。

以下示例演示了将数据插入到包含日/时间间隔和年/月时间间隔的表:

import java.sql.*;
import java.util.Properties;
// You need to import the Vertica JDBC classes to be able to instantiate
// the interval classes.
import com.vertica.jdbc.*;

public class IntervalDemo {
    public static void main(String[] args) {
        // If running under a Java 5 JVM, use you need to load the JDBC driver
        // using Class.forname here
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                    "jdbc:vertica://VerticaHost:5433/VMart", myProp);
            // Create table for interval values
            Statement stmt = conn.createStatement();
            stmt.execute("DROP TABLE IF EXISTS interval_demo");
            stmt.executeUpdate("CREATE TABLE interval_demo("
                    + "DayInt INTERVAL DAY TO SECOND, "
                    + "MonthInt INTERVAL YEAR TO MONTH)");
            // Insert data into interval columns using
            // VerticaDayTimeInterval and VerticaYearMonthInterval
            // classes.
            PreparedStatement pstmt = conn.prepareStatement(
                    "INSERT INTO interval_demo VALUES(?,?)");
            // Create instances of the Vertica classes that represent
            // intervals.
            VerticaDayTimeInterval dayInt = new VerticaDayTimeInterval(10, 0,
                    5, 40, 0, 0, false);
            VerticaYearMonthInterval monthInt = new VerticaYearMonthInterval(
                    10, 6, false);
            // These objects can also be manipulated using setters.
            dayInt.setHour(7);
            // Add the interval values to the batch
            ((VerticaPreparedStatement) pstmt).setObject(1, dayInt);
            ((VerticaPreparedStatement) pstmt).setObject(2, monthInt);
            pstmt.addBatch();
            // Set another row from strings.
            // Set day interval in "days HH:MM:SS" format
            pstmt.setString(1, "10 10:10:10");
            // Set year to month value in "MM-YY" format
            pstmt.setString(2, "12-09");
            pstmt.addBatch();
            // Execute the batch to insert the values.
            try {
                pstmt.executeBatch();
            } catch (SQLException e) {
                System.out.println("Error message: " + e.getMessage());
            }

读取时间间隔值

可以使用 ResultSet.getObject() 方法从结果集读取时间间隔值,然后将对象转换为适当的 Vertica 对象类: VerticaDayTimeInterval (代表日期/时间的时间间隔)或 VerticaYearMonthInterval(代表年/月时间间隔)。如果您知道列包含时间间隔并且知道该时间间隔的类型,则可以轻松完成此操作。如果应用程序无法确定从中读取的结果集的数据结构,您可以测试列是否包含特定于数据库的对象类型,如果是的话,则可以确定该对象属于 VerticaDayTimeInterval 类还是 VerticaYearMonthInterval 类。

            // Retrieve the interval values inserted by previous demo.
            // Query the table to get the row back as a result set.
            ResultSet rs = stmt.executeQuery("SELECT * FROM interval_demo");
            // If you do not know the types of data contained in the result set,
            // you can read its metadata to determine the type, and use
            // additional information to determine the interval type.
            ResultSetMetaData md = rs.getMetaData();
            while (rs.next()) {
                for (int x = 1; x <= md.getColumnCount(); x++) {
                    // Get data type from metadata
                    int colDataType = md.getColumnType(x);
                    // You can get the type in a string:
                    System.out.println("Column " + x + " is a "
                            + md.getColumnTypeName(x));
                    // Normally, you'd have a switch statement here to
                    // handle all sorts of column types, but this example is
                    // simplified to just handle database-specific types
                    if (colDataType == Types.OTHER) {
                        // Column contains a database-specific type. Determine
                        // what type of interval it is. Assuming it is an
                        // interval...
                        Object columnVal = rs.getObject(x);
                        if (columnVal instanceof VerticaDayTimeInterval) {
                            // We know it is a date time interval
                            VerticaDayTimeInterval interval =
                                    (VerticaDayTimeInterval) columnVal;
                            // You can use the getters to access the interval's
                            // data
                            System.out.print("Column " + x + "'s value is ");
                            System.out.print(interval.getDay() + " Days ");
                            System.out.print(interval.getHour() + " Hours ");
                            System.out.println(interval.getMinute()
                                    + " Minutes");
                        } else if (columnVal instanceof VerticaYearMonthInterval) {
                            VerticaYearMonthInterval interval =
                                    (VerticaYearMonthInterval) columnVal;
                            System.out.print("Column " + x + "'s value is ");
                            System.out.print(interval.getYear() + " Years ");
                            System.out.println(interval.getMonth() + " Months");
                        } else {
                            System.out.println("Not an interval.");
                        }
                    }
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

该示例将在控制台上输出以下内容:

Column 1 is a INTERVAL DAY TO SECOND
Column 1's value is 10 Days 7 Hours 5 Minutes
Column 2 is a INTERVAL YEAR TO MONTH
Column 2's value is 10 Years 6 Months
Column 1 is a INTERVAL DAY TO SECOND
Column 1's value is 10 Days 10 Hours 10 Minutes
Column 2 is a INTERVAL YEAR TO MONTH
Column 2's value is 12 Years 9 Months

另一个选项是使用数据库元数据来查找包含时间间隔的列。

// Determine the interval data types by examining the database
// metadata.
DatabaseMetaData dbmd = conn.getMetaData();
ResultSet dbMeta = dbmd.getColumns(null, null, "interval_demo", null);
int colcount = 0;
while (dbMeta.next()) {

    // Get the metadata type for a column.
    int javaType = dbMeta.getInt("DATA_TYPE");

    System.out.println("Column " + ++colcount + " Type name is " +
                    dbMeta.getString("TYPE_NAME"));

    if(javaType == Types.OTHER) {
      // The SQL_DATETIME_SUB column in the metadata tells you
      // Specifically which subtype of interval you have.
      // The VerticaDayTimeInterval.isDayTimeInterval()
      // methods tells you if that value is a day time.
      //
      int intervalType = dbMeta.getInt("SQL_DATETIME_SUB");
      if(VerticaDayTimeInterval.isDayTimeInterval(intervalType)) {
           // Now you know it is one of the 10 day/time interval types.
           // When you select this column you can cast to
           // VerticaDayTimeInterval.
           // You can get more specific by checking intervalType
           // against each of the 10 constants directly, but
           // they all are represented by the same object.
           System.out.println("column " + colcount + " is a " +
                           "VerticaDayTimeInterval intervalType = "
                          + intervalType);
      } else if(VerticaYearMonthInterval.isYearMonthInterval(
                      intervalType)) {
          //now you know it is one of the 3 year/month intervals,
          //and you can select the column and cast to
          // VerticaYearMonthInterval
          System.out.println("column " + colcount + " is a " +
                          "VerticaDayTimeInterval intervalType = "
                          + intervalType);
      } else {
          System.out.println("Not an interval type.");
      }
    }
}

2.3.3.3.4 - UUID 值

UUID 是 Vertica 中的核心数据类型。不过,它不是核心 Java 数据类型。您必须使用 java.util.UUID 类来表示 Java 代码中的 UUID 值。JDBC 驱动程序不会将 Vertica 中的值转换为非核心 Java 数据类型。因此,您必须使用通用对象方法(例如 PreparedStatement.setObject())将 UUID 值发送到 Vertica。您还可以使用通用对象方法(例如 ResultSet.getObject())从 Vertica 检索 UUID 值。然后,将检索到的对象转换为 java.util.UUID 类的成员。

以下示例代码演示如何将 UUID 值插入 Vertica 以及从 Vertica 中检索 UUID 值。

package jdbc_uuid_example;

import java.sql.*;
import java.util.Properties;

public class VerticaUUIDExample {

    public static void main(String[] args) {

        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "");
        Connection conn;

        try {
            conn = DriverManager.getConnection("jdbc:vertica://doch01:5433/VMart",
                                                myProp);
            Statement stmt = conn.createStatement();

            // Create a table with a UUID column and a VARCHAR column.
            stmt.execute("DROP TABLE IF EXISTS UUID_TEST CASCADE;");
            stmt.execute("CREATE TABLE UUID_TEST (id UUID, description VARCHAR(25));");

            // Prepare a statement to insert a UUID and a string into the table.
            PreparedStatement ps = conn.prepareStatement("INSERT INTO UUID_TEST VALUES(?,?)");

            java.util.UUID uuid;  // Holds the UUID value.

            for (Integer x = 0; x < 10; x++) {
                // Generate a random uuid
                uuid = java.util.UUID.randomUUID();
                // Set the UUID value by calling setObject.
                ps.setObject(1, uuid);
                // Set the String value to indicate which UUID this is.
                ps.setString(2, "UUID #" + x);
                ps.execute();
            }

            // Query the uuid
            ResultSet rs = stmt.executeQuery("SELECT * FROM UUID_TEST ORDER BY description ASC");
            while (rs.next()) {
                // Cast the object from the result set as a UUID.
                uuid = (java.util.UUID) rs.getObject(1);
                System.out.println(rs.getString(2) + " : " +  uuid.toString());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

以上示例生成类似如下的输出:

UUID #0 : 67b6dcb6-c28c-4965-b9f7-5c830a04664d
UUID #1 : 485d3835-2887-4233-b003-392254fa97e0
UUID #2 : 81421f51-c803-473d-8cfc-2c184582a117
UUID #3 : bec8b86a-b650-47b0-852c-8229155332d9
UUID #4 : 8ae5e3ec-d143-4ef7-8901-24f6d0483abf
UUID #5 : 669696ce-5e86-4e87-b8d0-a937f5fc18d7
UUID #6 : 19609ec9-ec56-4444-9cfe-ad2b8de537dd
UUID #7 : 97182e1d-5c7e-4da1-9922-67e804fde173
UUID #8 : c76c3a2b-a9ef-4d65-b2fb-7c637f872b3c
UUID #9 : 3cbbcd26-c177-4277-b3df-bf4d9389f69d

确定列是否具有 UUID 数据类型

JDBC 不支持 UUID 数据类型。此限制意味着您不能使用通常的 ResultSetMetaData.getColumnType() 方法来确定列的数据类型是否为 UUID。在 UUID 列上调用此方法会返回 Types.OTHER。该值也用于标识时间间隔列。可以使用两种方法来确定列是否包含 UUID:

  • 使用 ResultSetMetaData.getColumnTypeName() 获取列的数据类型的名称。对于 UUID 列,此方法将值 "Uuid" 作为String返回。

  • 查询表的元数据以获取列的 SQL 数据类型。如果此值等于 VerticaTypes.UUID,则该列的数据类型为 UUID。

下面的示例演示了全部两种方法:

            // This example assumes you already have a database connection
            // and result set from a query on a table that may contain a UUID.

            //  Get the metadata of the result set to get the column definitions
            ResultSetMetaData meta = rs.getMetaData();
            int colcount;
            int maxcol = meta.getColumnCount();

            System.out.println("Using column metadata:");
            for (colcount = 1; colcount < maxcol; colcount++) {
            // .getColumnType() always returns "OTHER" for UUID columns.
                if (meta.getColumnType(colcount) == Types.OTHER) {
                    // To determine that it is a UUID column, test the name of the column type.
                    if (meta.getColumnTypeName(colcount).equalsIgnoreCase("uuid")) {
                        // It's a UUID column
                        System.out.println("Column "+ colcount + " is UUID");
                    }
                }
            }

            // You can also query the table's metadata to find its column types and compare
            // it to the VerticaType.UUID constant to see if it is a UUID column.
            System.out.println("Using table metadata:");
            DatabaseMetaData dbmd = conn.getMetaData();
            // Get the metdata for the previously-created test table.
            ResultSet tableMeta = dbmd.getColumns(null, null, "UUID_TEST", null);
            colcount = 0;
            // Each row in the result set has metadata that describes a single column.
            while (tableMeta.next()) {
                colcount++;
                // The SQL_DATA_TYPE column holds the Vertica database data type. You compare
                // this value to the VerticvaTypes.UUID constant to see if it is a UUID.
                if (tableMeta.getInt("SQL_DATA_TYPE") == VerticaTypes.UUID) {
                    // Column is a UUID data type...
                    System.out.println("Column " + colcount + " is a UUID column.");
                }
            }

如果在运行上面的示例之后运行此示例,则会将以下内容输出到控制台:

Using column metadata:
Column 1 is UUID
Using table metadata:
Column 1 is a UUID column.

2.3.3.3.5 - JDBC 中的复杂类型

java.sql 查询的结果存储在 ResultSet 中。如果 ResultSet 包含复杂类型的列,您可以使用以下方法之一检索它:

  • 对于 ARRAYSETMAP 类型的列,使用 getArray(),它返回一个 java.sql.Array

  • 对于 ROW 类型的列,使用 getObject(),它返回一个 java.sql.Struct

类型转换表

java.sql.Arrayjava.sql.Struct 对象都有其自己的用于访问复杂类型数据的 API。在每种情况下,数据都以 java.lang.Object 的形式返回,其类型需要强制转换为 Java 类型。预期的确切 Java 类型取决于复杂类型定义中使用的 Vertica 类型,如下面的类型转换表所示:

ARRAY、SET 和 MAP 列

例如,以下方法运行返回某些 Vertica 类型的 ARRAY 的查询,然后在使用 getArray() 检索时这些类型由 JDBC 驱动程序强制转换为其对应 Java 类型的数组。此特定示例以 ARRAY[INT] 和 ARRAY[FLOAT] 开头,因此其类型分别强制转换为由类型转换表确定的 Long[]Double[]

  • getArrayResultSetExample() 显示了如何将 ARRAY 处理为 java.sql.ResultSet。此示例使用 getResultSet() 将底层数组作为另一个 ResultSet 返回。您可以使用此底层 ResultSet 执行以下操作:

    • 检索父 ResultSet

    • 将其视为 Object 数组或 ResultSet

  • getArrayObjectExample() 展示了如何将 ARRAY 作为原生 Java 数组进行处理。此示例使用 getArray() 将底层数组作为 Object 数组(而不是 ResultSet 数组)返回。它具有以下含义:

    • 您不能使用底层 Object 数组来检索其父数组。

    • 所有底层数组都被视为 Object 数组(而不是 ResultSet)。

package com.vertica.jdbc.test.samples;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.Array;
import java.sql.Struct;


public class ComplexTypesArraySamples
{
    /**
     * Executes a query and gets a java.sql.Array from the ResultSet. It then uses the Array#getResultSet
     * method to get a ResultSet containing the contents of the array.
     * @param conn A Connection to a Vertica database
     * @throws SQLException
     */
    public static void getArrayResultSetExample (Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();

        final String queryText = "SELECT ARRAY[ARRAY[1,2,3],ARRAY[4,5,6],ARRAY[7,8,9]]::ARRAY[ARRAY[INT]] as array";
        final String targetColumnName = "array";

        System.out.println ("queryText: " + queryText);
        ResultSet rs = stmt.executeQuery(queryText);
        int targetColumnId = rs.findColumn (targetColumnName);

        while (rs.next ()) {
            Array currentSqlArray = rs.getArray (targetColumnId);
            ResultSet level1ResultSet = currentSqlArray.getResultSet();
            if (level1ResultSet != null) {
                while (level1ResultSet.next ()) {
                    // The first column of the result set holds the row index
                    int i = level1ResultSet.getInt(1) - 1;
                    Array level2SqlArray = level1ResultSet.getArray (2);
                    Object level2Object = level2SqlArray.getArray ();
                    // For this ARRAY[INT], the driver returns a Long[]
                    assert (level2Object instanceof Long[]);
                    Long [] level2Array = (Long [])level2Object;
                    System.out.println (" level1Object [" + i + "]: " + level2SqlArray.toString () + " (" + level2SqlArray.getClass() + ")");

                    for (int j = 0; j < level2Array.length; j++) {
                       System.out.println (" Value [" + i + ", " + j + "]: " + level2Array[j] + " (" + level2Array[j].getClass() + ")");
                   }
                }
            }
        }
    }

    /**
     * Executes a query and gets a java.sql.Array from the ResultSet. It then uses the Array#getArray
     * method to get the contents of the array as a Java Object [].
     * @param conn A Connection to a Vertica database
     * @throws SQLException
     */
    public static void getArrayObjectExample (Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();

        final String queryText = "SELECT ARRAY[ARRAY[0.0,0.1,0.2],ARRAY[1.0,1.1,1.2],ARRAY[2.0,2.1,2.2]]::ARRAY[ARRAY[FLOAT]] as array";
        final String targetColumnName = "array";

        System.out.println ("queryText: " + queryText);
        ResultSet rs = stmt.executeQuery(queryText);
        int targetColumnId = rs.findColumn (targetColumnName);

        while (rs.next ()) {
            // Get the java.sql.Array from the result set
            Array currentSqlArray = rs.getArray (targetColumnId);
            // Get the internal Java Object implementing the array
            Object level1ArrayObject = currentSqlArray.getArray ();
            if (level1ArrayObject != null) {
                // All returned instances are Object[]
                assert (level1ArrayObject instanceof Object[]);
                Object [] level1Array = (Object [])level1ArrayObject;
                System.out.println ("Vertica driver returned a: " + level1Array.getClass());

                for (int i = 0; i < level1Array.length; i++) {
                    Object level2Object = level1Array[i];
                    // For this ARRAY[FLOAT], the driver returns a Double[]
                    assert (level2Object instanceof Double[]);
                    Double [] level2Array = (Double [])level2Object;
                    for (int j = 0; j < level2Array.length; j++) {
                         System.out.println (" Value [" + i + ", " + j + "]: " + level2Array[j] + " (" + level2Array[j].getClass() + ")");
                    }
                }
            }
        }
    }
}

getArrayResultSetExample() 的输出显示 Vertica 列类型 ARRAY[INT] 强制转换为 Long[]

queryText: SELECT ARRAY[ARRAY[1,2,3],ARRAY[4,5,6],ARRAY[7,8,9]]::ARRAY[ARRAY[INT]] as array
 level1Object [0]: [1,2,3] (class com.vertica.jdbc.jdbc42.S42Array)
 Value [0, 0]: 1 (class java.lang.Long)
 Value [0, 1]: 2 (class java.lang.Long)
 Value [0, 2]: 3 (class java.lang.Long)
 level1Object [1]: [4,5,6] (class com.vertica.jdbc.jdbc42.S42Array)
 Value [1, 0]: 4 (class java.lang.Long)
 Value [1, 1]: 5 (class java.lang.Long)
 Value [1, 2]: 6 (class java.lang.Long)
 level1Object [2]: [7,8,9] (class com.vertica.jdbc.jdbc42.S42Array)
 Value [2, 0]: 7 (class java.lang.Long)
 Value [2, 1]: 8 (class java.lang.Long)
 Value [2, 2]: 9 (class java.lang.Long)

getArrayObjectExample() 的输出显示 Vertica 列类型 ARRAY[FLOAT] 强制转换为 Double[]

queryText: SELECT ARRAY[ARRAY[0.0,0.1,0.2],ARRAY[1.0,1.1,1.2],ARRAY[2.0,2.1,2.2]]::ARRAY[ARRAY[FLOAT]] as array
Vertica driver returned a: class [Ljava.lang.Object;
 Value [0, 0]: 0.0 (class java.lang.Double)
 Value [0, 1]: 0.1 (class java.lang.Double)
 Value [0, 2]: 0.2 (class java.lang.Double)
 Value [1, 0]: 1.0 (class java.lang.Double)
 Value [1, 1]: 1.1 (class java.lang.Double)
 Value [1, 2]: 1.2 (class java.lang.Double)
 Value [2, 0]: 2.0 (class java.lang.Double)
 Value [2, 1]: 2.1 (class java.lang.Double)
 Value [2, 2]: 2.2 (class java.lang.Double)

ROW 列

如果在包含 ROW 类型的列的 java.sql.ResultSet 上调用 getObject(),会将该列检索为 java.sql.Struct,其中包含 Object[](本身可通过 getAttributes() 检索)。

Object[] 的每个元素代表该结构中的一个属性,每个属性都有对应的 Java 类型,如上面的类型转换表所示。

此示例定义具有以下属性的 ROW:


 Name    | Value        | Vertica Type | Java Type
-----------------------------------------------------------
 name    | Amy          | VARCHAR      | String
 date    | '07/10/2021' | DATE         | java.sql.Date
 id      | 5            | INT          | java.lang.Long
 current | false        | BOOLEAN      | java.lang.Boolean
package com.vertica.jdbc.test.samples;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.Array;
import java.sql.Struct;


public class ComplexTypesSamples
{
    /**
     * Executes a query and gets a java.sql.Struct from the ResultSet. It then uses the Struct#getAttributes
     * method to get the contents of the struct as a Java Object [].
     * @param conn A Connection to a Vertica database
     * @throws SQLException
     */
    public static void getRowExample (Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();

        final String queryText = "SELECT ROW('Amy', '07/10/2021'::Date, 5, false) as rowExample(name, date, id, current)";
        final String targetColumnName = "rowExample";

        System.out.println ("queryText: " + queryText);
        ResultSet rs = stmt.executeQuery(queryText);
        int targetColumnId = rs.findColumn (targetColumnName);

        while (rs.next ()) {
            // Get the java.sql.Array from the result set
            Object currentObject = rs.getObject (targetColumnId);
            assert (currentObject instanceof Struct);
            Struct rowStruct = (Struct)currentObject;

            Object[] attributes = rowStruct.getAttributes();

            // attributes.length should be 4 based on the queryText
            assert (attributes.length == 4);
            assert (attributes[0] instanceof String);
            assert (attributes[1] instanceof java.sql.Date);
            assert (attributes[2] instanceof java.lang.Long);
            assert (attributes[3] instanceof java.lang.Boolean);

            System.out.println ("attributes[0]: " + attributes[0] + " (" + attributes[0].getClass().getName() +")");
            System.out.println ("attributes[1]: " + attributes[1] + " (" + attributes[1].getClass().getName() +")");
            System.out.println ("attributes[2]: " + attributes[2] + " (" + attributes[2].getClass().getName() +")");
            System.out.println ("attributes[3]: " + attributes[3] + " (" + attributes[3].getClass().getName() +")");
        }
    }
}

getRowExample() 的输出显示了每个元素的属性及其对应的 Java 类型:


queryText: SELECT ROW('Amy', '07/10/2021'::Date, 5, false) as rowExample(name, date, id, current)
attributes[0]: Amy (java.lang.String)
attributes[1]: 2021-07-10 (java.sql.Date)
attributes[2]: 5 (java.lang.Long)
attributes[3]: false (java.lang.Boolean)

2.3.3.3.6 - JDBC 中的日期类型

将日期转换为字符串

就本页而言,大日期是指年份超过 9999 的日期。

如果您的数据库不包含任何大日期,则可以可靠地调用 toString() 以将日期转换为字符串。

否则,如果您的数据库包含大日期,您应当使用 java.text.SimpleDateFormat 及其 format() 方法:

  1. 使用 java.text.SimpleDateFormat 定义字符串格式。yyyy 格式的字符数定义了日期中使用的最小字符数。

  2. 调用 SimpleDateFormat.format() 以将 java.sql.Date 对象转换为字符串。

示例

例如,以下方法在将 java.sql.Date 对象作为实参传递时返回一个字符串。这里,格式的年份部分 YYYY 表示该格式兼容所有在年份中至少包含四个字符的日期。

#import java.sql.Date;

private String convertDate (Date date) {
    SimpleDateFormat dateFormat = new SimpleDateFormat ("yyyy-MM-dd");
    return dateFormat.format (date);
}

2.3.3.4 - 通过 JDBC 执行查询

若要通过 JDBC 运行查询,请执行下列操作:

  1. 连接到 Vertica 数据库。请参阅创建和配置连接

  2. 运行查询。

用于运行查询的方法取决于要运行的查询的类型:

  • 不返回结果集的 DDL 查询。

  • 将返回结果集的 DDL 查询。

  • DML 查询。

执行 DDL(数据定义语言)查询

若要运行诸如 CREATE TABLECOPY 等 DDL 查询,请使用 Statement.execute() 方法。可以通过调用连接对象的 createStatement 方法获取此类的实例。

以下示例将创建 Statement 类的实例,并使用该实例执行 CREATE TABLECOPY 查询:

Statement stmt = conn.createStatement();
stmt.execute("CREATE TABLE address_book (Last_Name char(50) default ''," +
    "First_Name char(50),Email char(50),Phone_Number char(50))");
stmt.execute("COPY address_book FROM 'address.dat' DELIMITER ',' NULL 'null'");

执行将返回结果集的查询

可以使用 Statement 类的executeQuery 方法执行将返回结果集的查询(例如 SELECT)。若要从结果集获取数据,请根据结果集中的列的数据类型使用诸如 getIntgetStringgetDouble 等方法访问列值。使用 ResultSet.next 可以前进到数据集的下一行。

ResultSet rs = null;
rs = stmt.executeQuery("SELECT First_Name, Last_Name FROM address_book");
int x = 1;
while(rs.next()){
    System.out.println(x + ". " + rs.getString(1).trim() + " "
                       + rs.getString(2).trim());
    x++;
}

使用 executeUpdate 执行 DML(数据操作语言)查询

可以对用于更改数据库中的数据的 DML SQL 查询(例如,INSERTUPDATEDELETE)使用 executeUpdate 方法,这些查询不会返回结果集。

stmt.executeUpdate("INSERT INTO address_book " +
                   "VALUES ('Ben-Shachar', 'Tamar', 'tamarrow@example.com'," +
                   "'555-380-6466')");
stmt.executeUpdate("INSERT INTO address_book (First_Name, Email) " +
                   "VALUES ('Pete','pete@example.com')");

执行存储过程

您可以使用 CallableStatement 来创建和执行存储过程

创建存储过程

Statement st = conn.createStatement();

String createSimpleSp = "CREATE OR REPLACE PROCEDURE raiseInt(IN x INT) LANGUAGE PLvSQL AS $$ " +
"BEGIN" +
    "RAISE INFO 'x = %', x;" +
"END;" +
"$$;";

st.execute(createSimpleSp);

调用存储过程:

String spCall = "CALL raiseInt (?)";
CallableStatement stmt = conn.prepareCall(spCall);
stmt.setInt(1, 42);

存储过程尚不支持 OUT 参数。相反,您可以分别使用 RAISEgetWarnings() 返回和检索执行信息:

System.out.println(stmt.getWarnings().toString());

2.3.3.5 - 取消 JDBC 查询

您可以使用 Statement.cancel() 方法取消 JDBC 查询。

以下示例创建一个表 jdbccanceltest 并运行两个查询,取消第一个查询:

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.Array;
import java.sql.Struct;


public class CancelSamples
{
    /**
     * Sets up a large test table, queries its contents and cancels the query.
     * @param  conn A connection to a Vertica database
     * @throws SQLException
     */
    public static void sampleCancelTest(Connection conn) throws SQLException
    {
        setup(conn);
        try
        {
            runQueryAndCancel(conn);
            runSecondQuery(conn);
        }
        finally
        {
            cleanup(conn);
        }
    }

    // Set up table used in test.
    private static void setup(Connection conn) throws SQLException
    {
        System.out.println("Creating and loading table...");
        Statement stmt = conn.createStatement();
        String queryText = "DROP TABLE IF EXISTS jdbccanceltest";
        stmt.execute(queryText);

        queryText = "CREATE TABLE jdbccanceltest(id INTEGER, time TIMESTAMP)";
        stmt.execute(queryText);

        queryText = "INSERT INTO jdbccanceltest SELECT row_number() OVER(), slice_time "
                    + "FROM(SELECT slice_time FROM("
                    + "SELECT '2021-01-01'::timestamp s UNION ALL SELECT '2022-01-01'::timestamp s"
                    + ") sq TIMESERIES slice_time AS '1 second' OVER(ORDER BY s)) sq2";
        stmt.execute(queryText);
    }

    /**
     * Execute a long-running query and cancel it.
     * @param  conn A connection to a Vertica database
     * @throws SQLException
     */
    private static void runQueryAndCancel(Connection conn) throws SQLException
    {
        System.out.println("Running and canceling query...");
        Statement stmt = conn.createStatement();
        String queryText = "select id, time from jdbccanceltest";
        ResultSet rs = stmt.executeQuery(queryText);

        int i=0;
        stmt.cancel();
        try
        {
            while (rs.next()) ;
            i++;
        }
        catch (SQLException e)
        {
            System.out.println("Query canceled after retrieving " + i + " rows");
            System.out.println(e.getMessage());
        }
    }

    /**
     * Run a simple query to demonstrate that it can be run after
     * the previous query was canceled.
     * @param conn A connection to a Vertica database
     * @throws SQLException
     */
    private static void runSecondQuery(Connection conn) throws SQLException
    {
        String queryText = "select 1 from dual";
        Statement stmt = conn.createStatement();
        try
        {
            ResultSet rs = stmt.executeQuery(queryText);
            while (rs.next()) ;
        }
        catch (SQLException e)
        {
            System.out.println(e.getMessage());
            System.out.println("warning: no exception should have been thrown on query after cancel");
        }
    }

    /**
     * Clean up table used in test.
     * @param conn A connetion to a Vertica database
     * @throws SQLException
     */
    private static void cleanup(Connection conn) throws SQLException
    {
        String queryText = "drop table if exists jdbccanceltest";
        Statement stmt = conn.createStatement();
        stmt.execute(queryText);
    }
}

2.3.3.6 - 通过 JDBC 加载数据

可以使用以下任意方法通过 JDBC 接口加载数据:

  • 执行 SQL INSERT 语句以直接插入单个行。

  • 使用预定义的语句批量加载数据。

  • 使用 COPY 批量加载文件或流中的数据。

以下几节详细介绍如何使用 JDBC 加载数据。

2.3.3.6.1 - 使用单行插入

将数据插入到表中的最简单方法是使用 SQL INSERT 语句。可以将 Statement 类的成员实例化以使用此语句,并使用其 executeUpdate() 方法以运行 SQL 语句。

以下代码片段演示了如何创建 Statement 对象并使用该对象将数据插入到名为 address_book 的表中:

Statement stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO address_book " +
             "VALUES ('Smith', 'John', 'jsmith@example.com', " +
             "'555-123-4567')");

此方法有几个缺点:您需要将数据转换为字符串并对数据中的特殊字符进行转义。插入数据的更好方法是使用预定义的语句。请参阅使用 JDBC 预定义的语句批量插入

2.3.3.6.2 - 使用 JDBC 预定义的语句批量插入

可以使用预定义的 INSERT 语句(仅需设置一次即可重复调用的服务器端语句)将数据批量加载到 Vertica 中。您可以使用包含表示数据的问号占位符的 SQL 语句将 PreparedStatement 类的成员实例化。例如:

PreparedStatement pstmt = conn.prepareStatement(
                    "INSERT INTO customers(last, first, id) VALUES(?,?,?)");

然后,可以对 PreparedStatement 对象使用特定于数据类型的方法(例如 setString()setInt())来设置参数。设置参数后,调用 addBatch() 方法以将行添加到批中。整个数据批准备就绪后,调用 executeBatch() 方法以执行批量插入。

在后台,批量插入会转换为 COPY 语句。如果已禁用连接的 AutoCommit 参数,则 Vertica 会保持打开 COPY 语句并使用该语句加载后续的批,直至事务已提交或者游标已关闭或应用程序执行任何其他操作(或者使用其他 StatementPreparedStatement 对象执行任何语句)为止。使用单个 COPY 语句进行多个批量插入可以更高效地加载数据。如果要加载多个批,应禁用数据库的 AutoCommit 属性以提高效率。

执行批量插入时,可以试验各个批大小和行大小以确定可提供最佳性能的设置。

以下示例演示了使用预定义的语句批量插入数据。

import java.sql.*;
import java.util.Properties;

public class BatchInsertExample {
    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");

     //Set streamingBatchInsert to True to enable streaming mode for batch inserts.
     //myProp.put("streamingBatchInsert", "True");

     Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);
            // establish connection and make a table for the data.
            Statement stmt = conn.createStatement();


            // Set AutoCommit to false to allow Vertica to reuse the same
            // COPY statement
            conn.setAutoCommit(false);


            // Drop table and recreate.
            stmt.execute("DROP TABLE IF EXISTS customers CASCADE");
            stmt.execute("CREATE TABLE customers (CustID int, Last_Name"
                            + " char(50), First_Name char(50),Email char(50), "
                            + "Phone_Number char(12))");
            // Some dummy data to insert.
            String[] firstNames = new String[] { "Anna", "Bill", "Cindy",
                            "Don", "Eric" };
            String[] lastNames = new String[] { "Allen", "Brown", "Chu",
                            "Dodd", "Estavez" };
            String[] emails = new String[] { "aang@example.com",
                            "b.brown@example.com", "cindy@example.com",
                            "d.d@example.com", "e.estavez@example.com" };
            String[] phoneNumbers = new String[] { "123-456-7890",
                            "555-444-3333", "555-867-5309",
                            "555-555-1212", "781-555-0000" };
            // Create the prepared statement
            PreparedStatement pstmt = conn.prepareStatement(
                            "INSERT INTO customers (CustID, Last_Name, " +
                            "First_Name, Email, Phone_Number)" +
                            " VALUES(?,?,?,?,?)");
            // Add rows to a batch in a loop. Each iteration adds a
            // new row.
            for (int i = 0; i < firstNames.length; i++) {
                // Add each parameter to the row.
                pstmt.setInt(1, i + 1);
                pstmt.setString(2, lastNames[i]);
                pstmt.setString(3, firstNames[i]);
                pstmt.setString(4, emails[i]);
                pstmt.setString(5, phoneNumbers[i]);
                // Add row to the batch.
                pstmt.addBatch();
            }

            try {
                // Batch is ready, execute it to insert the data
                pstmt.executeBatch();
            } catch (SQLException e) {
                System.out.println("Error message: " + e.getMessage());
                return; // Exit if there was an error
            }

            // Commit the transaction to close the COPY command
            conn.commit();


            // Print the resulting table.
            ResultSet rs = null;
            rs = stmt.executeQuery("SELECT CustID, First_Name, "
                            + "Last_Name FROM customers ORDER BY CustID");
            while (rs.next()) {
                System.out.println(rs.getInt(1) + " - "
                                + rs.getString(2).trim() + " "
                                + rs.getString(3).trim());
            }
            // Cleanup
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行此示例代码后的结果如下所示:

1 - Anna Allen
2 - Bill Brown
3 - Cindy Chu
4 - Don Dodd
5 - Eric Estavez

流式批量插入

默认情况下,Vertica 通过缓存每个行并在用户调用 executeBatch() 方法时插入缓存来执行批量插入。Vertica 也支持流式批量插入。流式批量插入在用户每次调用 addBatch() 时将行添加到数据库中。流式批量插入可提高数据库性能,因为它能够进行并行处理并降低内存需求。

若要启用流式批量插入,请将 streamingBatchInsert 属性设置为 True。以上代码示例包含一个行,该行启用了 streamingBatchInsert 模式。移除 // 注释标记可启用该行并激活流式批量插入。

下表介绍了各种批量插入方法及其默认批量插入模式和流式批量插入模式之间的行为差异。

注意

  • 使用 PreparedStatement.setFloat() 方法会导致出现舍入误差。如果精度很重要,请改为使用 .setDouble() 方法。

  • 预定义语句时,PreparedStatement 对象会缓存连接的 AutoCommit 属性。以后对 AutoCommit 属性进行的更改不会影响预定义的语句。

2.3.3.6.2.1 - 批量加载过程中的错误处理

加载各个批时,您可以确定已接受的行数和已拒绝了哪些行(有关详细信息,请参阅确定已接受的行和已拒绝的行)。如果禁用了 AutoCommit 连接设置,则插入各个批时不会发生其他错误(例如磁盘空间错误)。此行为是由单个 SQL COPY 语句对多个连续批执行加载(这样可提高加载过程的效率)导致的。只有 COPY 语句关闭时,才会提交批量数据,而且 Vertica 会报告其他类型错误。

因此,应使批量加载应用程序准备好在 COPY 语句关闭时检查错误。可以通过以下方法促使 COPY 语句关闭:

  • 通过调用 Connection.commit() 来结束批量加载事务

  • 通过使用 Statement.close() 来关闭该语句

  • 在加载中插入最后一批之前将连接的 AutoCommit 属性设置为 true

2.3.3.6.2.2 - 确定已接受和已拒绝行 (JDBC)

PreparedStatement.executeBatch 的返回值是一个整数数组,其中包含每个行的插入操作的成功或失败状态。值 1 表示行已被接受,而值 -3 表示行已被拒绝。如果在批处理执行期间出现了异常,您还可以使用 BatchUpdateException.getUpdateCounts() 获取该数组。

以下示例延伸了 使用 JDBC 预定义的语句批量插入 中所示的示例,以检索该数组并显示批量加载的结果。

import java.sql.*;
import java.util.Arrays;
import java.util.Properties;

public class BatchInsertErrorHandlingExample {
    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");
        Connection conn;

        // establish connection and make a table for the data.
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);


            // Disable auto commit
            conn.setAutoCommit(false);

            // Create a statement
            Statement stmt = conn.createStatement();
            // Drop table and recreate.
            stmt.execute("DROP TABLE IF EXISTS customers CASCADE");
            stmt.execute("CREATE TABLE customers (CustID int, Last_Name"
                            + " char(50), First_Name char(50),Email char(50), "
                            + "Phone_Number char(12))");

            // Some dummy data to insert. The one row won't insert because
            // the phone number is too long for the phone column.
            String[] firstNames = new String[] { "Anna", "Bill", "Cindy",
                            "Don", "Eric" };
            String[] lastNames = new String[] { "Allen", "Brown", "Chu",
                            "Dodd", "Estavez" };
            String[] emails = new String[] { "aang@example.com",
                            "b.brown@example.com", "cindy@example.com",
                            "d.d@example.com", "e.estavez@example.com" };
            String[] phoneNumbers = new String[] { "123-456-789",
                            "555-444-3333", "555-867-53093453453",
                            "555-555-1212", "781-555-0000" };

            // Create the prepared statement
            PreparedStatement pstmt = conn.prepareStatement(
                            "INSERT INTO customers (CustID, Last_Name, " +
                            "First_Name, Email, Phone_Number)" +
                            " VALUES(?,?,?,?,?)");

            // Add rows to a batch in a loop. Each iteration adds a
            // new row.
            for (int i = 0; i < firstNames.length; i++) {
                // Add each parameter to the row.
                pstmt.setInt(1, i + 1);
                pstmt.setString(2, lastNames[i]);
                pstmt.setString(3, firstNames[i]);
                pstmt.setString(4, emails[i]);
                pstmt.setString(5, phoneNumbers[i]);
                // Add row to the batch.
                pstmt.addBatch();
            }

            // Integer array to hold the results of inserting
            // the batch. Will contain an entry for each row,
            // indicating success or failure.
            int[] batchResults = null;

            try {
                // Batch is ready, execute it to insert the data
                batchResults = pstmt.executeBatch();
            } catch (BatchUpdateException e) {
                // We expect an exception here, since one of the
                // inserted phone numbers is too wide for its column. All of the
                // rest of the rows will be inserted.
                System.out.println("Error message: " + e.getMessage());

                // Batch results isn't set due to exception, but you
                // can get it from the exception object.
                //
                // In your own code, you shouldn't assume the a batch
                // exception occurred, since exceptions can be thrown
                // by the server for a variety of reasons.
                batchResults = e.getUpdateCounts();
            }
            // You should also be prepared to catch SQLExceptions in your own
            // application code, to handle dropped connections and other general
            // problems.

            // Commit the transaction
            conn.commit();


            // Print the array holding the results of the batch insertions.
            System.out.println("Return value from inserting batch: "
                            + Arrays.toString(batchResults));
            // Print the resulting table.
            ResultSet rs = null;
            rs = stmt.executeQuery("SELECT CustID, First_Name, "
                            + "Last_Name FROM customers ORDER BY CustID");
            while (rs.next()) {
                System.out.println(rs.getInt(1) + " - "
                                + rs.getString(2).trim() + " "
                                + rs.getString(3).trim());
            }

            // Cleanup
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行以上示例后,将在控制台上生成以下输出:

Error message: [Vertica][VJDBC](100172) One or more rows were rejected by the server.Return value from inserting batch: [1, 1, -3, 1, 1]
1 - Anna Allen
2 - Bill Brown
4 - Don Dodd
5 - Eric Estavez

请注意,第三行插入失败,因为其电话号码对 Phone_Number 列来说太长。该批中其余的所有行(包括该错误之后的行)已正确插入。

2.3.3.6.2.3 - 在服务器中回退批量加载

即使一个或多个行被拒绝,批量加载也始终插入所有数据。只有某个批中导致发生错误的行不会加载。如果数据库连接的 AutoCommit 属性为 true,各个批会在完成后自动提交其事务,因此在该批完成加载后,将提交数据。

在某些情况下,您可能希望成功插入某个批中的所有数据(在发生错误时不应提交任何数据)。若要实现此目的,最佳方法是关闭数据库连接的 AutoCommit 属性以防止各个批自动提交其自身。然后,如果某个批遇到错误,您可以在捕获由插入错误导致的 BatchUpdateException 之后回退事务。

以下示例演示了在加载某个批期间发生任何错误时执行回退。

import java.sql.*;
import java.util.Arrays;
import java.util.Properties;

public class RollbackBatchOnError {
    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);
            // Disable auto-commit. This will allow you to roll back a
            // a batch load if there is an error.
            conn.setAutoCommit(false);
            // establish connection and make a table for the data.
            Statement stmt = conn.createStatement();
            // Drop table and recreate.
            stmt.execute("DROP TABLE IF EXISTS customers CASCADE");
            stmt.execute("CREATE TABLE customers (CustID int, Last_Name"
                            + " char(50), First_Name char(50),Email char(50), "
                            + "Phone_Number char(12))");

            // Some dummy data to insert. The one row won't insert because
            // the phone number is too long for the phone column.
            String[] firstNames = new String[] { "Anna", "Bill", "Cindy",
                            "Don", "Eric" };
            String[] lastNames = new String[] { "Allen", "Brown", "Chu",
                            "Dodd", "Estavez" };
            String[] emails = new String[] { "aang@example.com",
                            "b.brown@example.com", "cindy@example.com",
                            "d.d@example.com", "e.estavez@example.com" };
            String[] phoneNumbers = new String[] { "123-456-789",
                            "555-444-3333", "555-867-53094535", "555-555-1212",
                            "781-555-0000" };
            // Create the prepared statement
            PreparedStatement pstmt = conn.prepareStatement(
                            "INSERT INTO customers (CustID, Last_Name, " +
                            "First_Name, Email, Phone_Number) "+
                            "VALUES(?,?,?,?,?)");
            // Add rows to a batch in a loop. Each iteration adds a
            // new row.
            for (int i = 0; i < firstNames.length; i++) {
                // Add each parameter to the row.
                pstmt.setInt(1, i + 1);
                pstmt.setString(2, lastNames[i]);
                pstmt.setString(3, firstNames[i]);
                pstmt.setString(4, emails[i]);
                pstmt.setString(5, phoneNumbers[i]);
                // Add row to the batch.
                pstmt.addBatch();
            }
            // Integer array to hold the results of inserting
            // the batch. Will contain an entry for each row,
            // indicating success or failure.
            int[] batchResults = null;
            try {
                // Batch is ready, execute it to insert the data
                batchResults = pstmt.executeBatch();
                // If we reach here, we inserted the batch without errors.
                // Commit it.
                System.out.println("Batch insert successful. Committing.");
                conn.commit();
            } catch (BatchUpdateException e) {
                    System.out.println("Error message: " + e.getMessage());
                    // Batch results isn't set due to exception, but you
                    // can get it from the exception object.
                    batchResults =  e.getUpdateCounts();
                    // Roll back the batch transaction.
                    System.out.println("Rolling back batch insertion");
                    conn.rollback();
            }
            catch  (SQLException e) {
                // General SQL errors, such as connection issues, throw
                // SQLExceptions. Your application should do something more
                // than just print a stack trace,
                e.printStackTrace();
            }
            System.out.println("Return value from inserting batch: "
                            + Arrays.toString(batchResults));
            System.out.println("Customers table contains:");


            // Print the resulting table.
            ResultSet rs = null;
            rs = stmt.executeQuery("SELECT CustID, First_Name, "
                            + "Last_Name FROM customers ORDER BY CustID");
            while (rs.next()) {
                System.out.println(rs.getInt(1) + " - "
                                + rs.getString(2).trim() + " "
                                + rs.getString(3).trim());
            }

            // Cleanup
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行以上示例后,将在系统控制台上输出以下内容:

Error message: [Vertica][VJDBC](100172) One or more rows were rejected by the server.Rolling back batch insertion
Return value from inserting batch: [1, 1, -3, 1, 1]
Customers table contains:

返回值指示是否已成功插入每个行。值 1 表示已插入该行并且未出现任何问题,而值 -3 表示插入该行失败。

客户表为空,因为已回退该批量插入,原因是第三列导致发生错误。

2.3.3.6.3 - 使用 COPY 语句进行批量加载

一次将大量数据加载(批量加载)到 Vertica 的最快方法之一是使用 COPY 语句。此语句可将存储在 Vertica 主机上的文件中的数据(或数据流中的数据)加载到数据库中的表。可以向 COPY 语句传递定义了以下设置的参数:文件中的数据的格式、加载数据时对数据的转换方式、对错误的处理方式以及加载数据的方式。有关详细信息,请参阅 COPY 文档。

只有 超级用户可以使用 COPY 语句复制存储在主机上的文件,因此您必须使用超级用户帐户连接到数据库。如果要以非超级用户身份批量加载数据,您可以使用 COPY 从主机上的流(例如 STDIN)而非文件加载数据,或者也可以从客户端以流式传输数据(请参阅通过 JDBC 进行流式数据传输)。您还可以执行使用预定义的语句的批量插入(此为标准方式),这种方式在后台使用 COPY 语句来加载数据。

以下示例演示了通过 JDBC 使用 COPY 语句将名为 customers.txt 的文件加载到新的数据库表。该文件必须存储在应用程序连接到的数据库主机(在此示例中是名为 VerticaHost 的主机)上。

import java.sql.*;
import java.util.Properties;
import com.vertica.jdbc.*;

public class COPYFromFile {
    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "ExampleAdmin"); // Must be superuser
        myProp.put("password", "password123");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",myProp);
            // Disable AutoCommit
            conn.setAutoCommit(false);
            Statement stmt = conn.createStatement();
            // Create a table to hold data.
            stmt.execute("DROP TABLE IF EXISTS customers;");
            stmt.execute("CREATE TABLE IF NOT EXISTS customers (Last_Name char(50) "
                            + "NOT NULL, First_Name char(50),Email char(50), "
                            + "Phone_Number char(15))");

             // Use the COPY command to load data. Use ENFORCELENGTH to reject
            // strings too wide for their columns.
            boolean result = stmt.execute("COPY customers FROM "
                            + " '/data/customers.txt' ENFORCELENGTH");

            // Determine if execution returned a count value, or a full result
            // set.
            if (result) {
                System.out.println("Got result set");
            } else {
                // Count will usually return the count of rows inserted.
                System.out.println("Got count");
                int rowCount = stmt.getUpdateCount();
                System.out.println("Number of accepted rows = " + rowCount);
            }


            // Commit the data load
            conn.commit();
        } catch (SQLException e) {
            System.out.print("Error: ");
            System.out.println(e.toString());
        }
    }
}

运行此示例后,将在系统控制台上输出以下内容(假设 customers.txt 文件两百万个有效的行):

Number of accepted rows = 2000000

2.3.3.6.4 - 通过 JDBC 进行流式数据传输

以下两个选项可用于将客户端上的文件中的数据以流式传输到 Vertica 数据库:

  • 使用 VerticaCopyStream 类按照面向对象的方式流式传输数据 - 有关该类的详细信息,请参阅 JDBC 文档

  • 执行 COPY LOCAL SQL 语句以进行流式数据传输

此部分中的主题介绍了使用这些选项的方法。

2.3.3.6.4.1 - 使用 VerticaCopyStream

使用 VerticaCopyStream 类可以将数据从客户端系统流式传输到 Vertica 数据库。它允许您直接使用 COPY,而无需先将数据复制到数据库群集中的主机。使用 COPY 命令从主机加载数据时,需要拥有超级用户权限才能访问主机的文件系统。用于从流加载数据的 COPY 语句不需要超级用户权限,因此客户端可以使用对目标表拥有 INSERT 权限的任何用户帐户进行连接。

若要将流复制到数据库,请执行下列操作:

  1. 禁用数据库连接的 AutoCommit 连接参数。

  2. VerticaCopyStreamObject 实例化,并至少向其传递数据库连接对象和包含用于加载数据的 COPY 语句的字符串。该语句必须将 STDIN 中的数据复制到表。您可以使用适用于数据加载的任何参数。

  3. 调用 VerticaCopyStreamObject.start(),以启动 COPY 语句并开始以流式传输已添加到 VerticaCopyStreamObject 的任何流中的数据。

  4. 调用 VerticaCopyStreamObject.addStream(),以将其他流添加到要发送到数据库的流的列表。然后,可以调用 VerticaCopyStreamObject.execute() 以将它们以流式传输到服务器。

  5. (可选)调用 VerticaCopyStreamObject.getRejects() 以从上一次 .execute() 调用获取拒绝的行的列表。对 .execute().finish() 的每次调用会重置拒绝列表。

  6. 完成添加流后,调用 VerticaCopyStreamObject.finish() 以将其余任何流发送到数据库并关闭 COPY 语句。

  7. 调用 Connection.commit() 以提交已加载的数据。

获取拒绝的行

VerticaCopyStreamObject.getRejects() 方法将返回一个列表,其中包含上一次 .execute() 方法调用之后拒绝的行的数量。对 .execute() 的每次调用会清除拒绝的行的列表,因此您需要在每次调用 .execute() 之后调用 .getRejects()。由于 .start().finish() 也会调用 .execute() 以将任何挂起的流发送到服务器,因此您还应在调用这些方法之后调用 .getRejects()

以下示例演示了将存储在客户端系统上的五个文本文件的内容加载到表中。

import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import com.vertica.jdbc.VerticaConnection;
import com.vertica.jdbc.VerticaCopyStream;

public class CopyMultipleStreamsExample {
    public static void main(String[] args) {
        // Note: If running on Java 5, you need to call Class.forName
        // to manually load the JDBC driver.
        // Set up the properties of the connection
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser"); // Must be superuser
        myProp.put("password", "password123");
        // When performing bulk loads, you should always disable the
        // connection's AutoCommit property to ensure the loads happen as
        // efficiently as possible by reusing the same COPY command and
        // transaction.
        myProp.put("AutoCommit", "false");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                          "jdbc:vertica://VerticaHost:5433/ExampleDB", myProp);
            Statement stmt = conn.createStatement();

            // Create a table to receive the data
            stmt.execute("DROP TABLE IF EXISTS customers");
            stmt.execute("CREATE TABLE customers (Last_Name char(50), "
                            + "First_Name char(50),Email char(50), "
                            + "Phone_Number char(15))");

            // Prepare the query to insert from a stream. This query must use
            // the COPY statement to load data from STDIN. Unlike copying from
            // a file on the host, you do not need superuser privileges to
            // copy a stream. All your user account needs is INSERT privileges
            // on the target table.
            String copyQuery = "COPY customers FROM STDIN "
                            + "DELIMITER '|' ENFORCELENGTH";

            // Create an instance of the stream class. Pass in the
            // connection and the query string.
            VerticaCopyStream stream = new VerticaCopyStream(
                            (VerticaConnection) conn, copyQuery);

            // Keep running count of the number of rejects
            int totalRejects = 0;

            // start() starts the stream process, and opens the COPY command.
            stream.start();

            // If you added streams to VerticaCopyStream before calling start(),
            // You should check for rejects here (see below). The start() method
            // calls execute() to send any pre-queued streams to the server
            // once the COPY statement has been created.

            // Simple for loop to load 5 text files named customers-1.txt to
            // customers-5.txt
            for (int loadNum = 1; loadNum <= 5; loadNum++) {
                // Prepare the input file stream. Read from a local file.
                String filename = "C:\\Data\\customers-" + loadNum + ".txt";
                System.out.println("\n\nLoading file: " + filename);
                File inputFile = new File(filename);
                FileInputStream inputStream = new FileInputStream(inputFile);

                // Add stream to the VerticaCopyStream
                stream.addStream(inputStream);

                // call execute() to load the newly added stream. You could
                // add many streams and call execute once to load them all.
                // Which method you choose depends mainly on whether you want
                // the ability to check the number of rejections as the load
                // progresses so you can stop if the number of rejects gets too
                // high. Also, high numbers of InputStreams could create a
                // resource issue on your client system.
                stream.execute();

                // Show any rejects from this execution of the stream load
                // getRejects() returns a List containing the
                // row numbers of rejected rows.
                List<Long> rejects = stream.getRejects();

                // The size of the list gives you the number of rejected rows.
                int numRejects = rejects.size();
                totalRejects += numRejects;
                System.out.println("Number of rows rejected in load #"
                                + loadNum + ": " + numRejects);

                // List all of the rows that were rejected.
                Iterator<Long> rejit = rejects.iterator();
                long linecount = 0;
                while (rejit.hasNext()) {
                    System.out.print("Rejected row #" + ++linecount);
                    System.out.println(" is row " + rejit.next());
                }
            }
            // Finish closes the COPY command. It returns the number of
            // rows inserted.
            long results = stream.finish();
            System.out.println("Finish returned " + results);

            // If you added any streams that hadn't been executed(),
            // you should also check for rejects here, since finish()
            // calls execute() to

            // You can also get the number of rows inserted using
            // getRowCount().
            System.out.println("Number of rows accepted: "
                            + stream.getRowCount());
            System.out.println("Total number of rows rejected: " + totalRejects);

            // Commit the loaded data
            conn.commit();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

对部分示例数据运行以上示例后,将生成以下输出:


Loading file: C:\Data\customers-1.txtNumber of rows rejected in load #1: 3
Rejected row #1 is row 3
Rejected row #2 is row 7
Rejected row #3 is row 51
Loading file: C:\Data\customers-2.txt
Number of rows rejected in load #2: 5Rejected row #1 is row 4143
Rejected row #2 is row 6132
Rejected row #3 is row 9998
Rejected row #4 is row 10000
Rejected row #5 is row 10050
Loading file: C:\Data\customers-3.txt
Number of rows rejected in load #3: 9
Rejected row #1 is row 14142
Rejected row #2 is row 16131
Rejected row #3 is row 19999
Rejected row #4 is row 20001
Rejected row #5 is row 20005
Rejected row #6 is row 20049
Rejected row #7 is row 20056
Rejected row #8 is row 20144
Rejected row #9 is row 20236
Loading file: C:\Data\customers-4.txt
Number of rows rejected in load #4: 8
Rejected row #1 is row 23774
Rejected row #2 is row 24141
Rejected row #3 is row 25906
Rejected row #4 is row 26130
Rejected row #5 is row 27317
Rejected row #6 is row 28121
Rejected row #7 is row 29321
Rejected row #8 is row 29998
Loading file: C:\Data\customers-5.txt
Number of rows rejected in load #5: 1
Rejected row #1 is row 39997
Finish returned 39995
Number of rows accepted: 39995
Total number of rows rejected: 26

2.3.3.6.4.2 - 将 COPY LOCAL 与 JDBC 配合使用

若要将 COPY LOCAL 与 JDBC 结合使用,只需执行 COPY LOCAL 语句并指定客户端系统上源文件的路径即可。此方法比使用 VerticaCopyStream 类更简单(有关该类的详细信息,请参阅 JDBC 文档)。但是,如果有许多文件要复制到数据库,或者如果数据来自某个文件以外的其他源(例如,通过网络连接进行流式传输),您可能倾向于使用 VerticaCopyStream

可以在多语句查询中使用 COPY LOCAL。但是,您应当始终将其作为查询中的第一条语句。不应当在同一个查询中多次使用它。

以下示例代码演示了使用 COPY LOCAL 将文件从客户端复制到数据库。除了在 COPY 语句中使用 LOCAL 选项,并且数据文件的路径位于客户端系统而非服务器上,此示例与使用 COPY 语句进行批量加载中所示的代码相同。

import java.sql.*;
import java.util.Properties;

public class COPYLocal {
    public static void main(String[] args) {
        // Note: If using Java 5, you must call Class.forName to load the
        // JDBC driver.
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser"); // Do not need to superuser
        myProp.put("password", "password123");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",myProp);
            // Disable AutoCommit
            conn.setAutoCommit(false);
            Statement stmt = conn.createStatement();
            // Create a table to hold data.
            stmt.execute("DROP TABLE IF EXISTS customers;");
            stmt.execute("CREATE TABLE IF NOT EXISTS customers (Last_Name char(50) "
                            + "NOT NULL, First_Name char(50),Email char(50), "
                            + "Phone_Number char(15))");

            // Use the COPY command to load data. Load directly into ROS, since
            // this load could be over 100MB. Use ENFORCELENGTH to reject
            // strings too wide for their columns.
            boolean result = stmt.execute("COPY customers FROM LOCAL "
                            + " 'C:\\Data\\customers.txt' DIRECT ENFORCELENGTH");

            // Determine if execution returned a count value, or a full result
            // set.
            if (result) {
                System.out.println("Got result set");
            } else {
                // Count will usually return the count of rows inserted.
                System.out.println("Got count");
                int rowCount = stmt.getUpdateCount();
                System.out.println("Number of accepted rows = " + rowCount);
            }

            conn.close();
        } catch (SQLException e) {
            System.out.print("Error: ");
            System.out.println(e.toString());
        }
    }
}

运行此代码后的结果如下所示。在此示例中,customers.txt 文件包含 10000 行,其中七个行被拒绝,因为这些行所包含的数据的宽度太大而无法适应其数据库列。

Got countNumber of accepted rows = 9993

2.3.3.7 - 处理错误

当 Vertica JDBC 驱动程序遇到错误时,它会引发 SQLException 或其子类之一。所引发的特定子类取决于已发生的错误的类型。大部分 JDBC 方法调用会导致发生多种不同类型的错误,JDBC 驱动程序将引发特定的 SQLException 子类以响应这些错误。客户端应用程序可以根据 JDBC 驱动程序已引发的特定异常选择如何对错误做出响应。

SQLException 子类的层次结构已进行了排列,可帮助客户端应用程序确定采取哪些措施以对错误条件做出响应。例如:

  • 当错误原因可能是临时条件(例如,超时错误 (SQLTimeoutException) 或连接问题 (SQLTransientConnectionIssue))时,JDBC 驱动程序将引发 SQLTransientException 子类。客户端应用程序可以选择重试操作而不进行任何尝试以纠正错误,因为该错误可能不会再次发生。

  • 当客户端需要采取某种措施以便能够重试操作时,JDBC 驱动程序将引发 SQLNonTransientException 子类。例如,执行包含 SQL 语法错误的语句会导致 JDBC 驱动程序引发 SQLSyntaxErrorExceptionSQLNonTransientException 的子类)。通常,客户端应用程序只需要将这些错误报告回给用户并让用户解决错误。例如,如果用户向应用程序提供的 SQL 语句触发了 SQLSyntaxErrorException,则应用程序会提示用户修复 SQL 错误。

有关由 JDBC 驱动程序引发的 Java 异常的列表,请参阅 SQLState 和 Java 异常类的映射

2.3.3.7.1 - SQLState 和 Java 异常类的映射

2.3.3.8 - 将 JDBC 查询直接路由到单个节点

JDBC 驱动程序能够使用名为可路由连接的特殊连接将查询直接路由到单个节点。此功能适用于大量的“短”请求,此类请求将返回全部存在于单个节点上的少量结果。此功能的常见使用场景是对由唯一键标识的数据执行大量查找。与分布式查询相比,可路由查询通常提供更低延迟并使用更少系统资源。但是,必须以某种方式对要查询的数据进行分段,以使 JDBC 客户端能够确定数据驻留在哪个节点。

Vertica 典型分析查询

典型分析查询需要对群集中所有节点上的数据执行密集计算,并且通过让所有节点参与查询的规划和执行来获益。

Vertica 可路由查询 API 查询

对于返回单个或几个数据行的大量查询,在包含数据的单个节点上执行查询更高效。

为有效地将请求路由到单个节点,客户端必须确定数据所驻留在的特定节点。为使客户端能够确定正确节点,必须按一个或多个列对表进行分段。例如,如果按主键 (PK) 列对表进行分段,则客户端可以基于主键确定数据驻留在哪个节点,并直接连接到该节点以快速完成请求。

可路由查询 API 提供以下两个用于执行可路由查询的类:VerticaRoutableExecutor 和 VGet。VerticaRoutableExecutor 提供更富有表现力的基于 SQL 的 API,而 VGet 提供结构更完善的 API 用于编程访问。

  • 通过 VerticaRoutableExecutor 类,您可以将传统 SQL 与减少的功能集结合使用,以在单个节点上查询数据。

    对于联接,表必须按存在于要联接的每个表中的键列进行联接,并且这些表必须按该键进行分段。但是,此限制对未分段的表不适用,这种表始终可以联接(因为未分段的表中的所有数据在所有节点上均可用)。

  • VGet 类不使用传统 SQL 语法。相反,此类使用您通过定义谓词而构建的数据结构,还使用谓词表达式以及输出和输出表达式。此类适用于对单个表执行键/值类型查找。

    用于查询表的数据结构必须为该表的投影中定义的每个已分段的列提供一个谓词。您至少必须为每个已分段的列提供一个具有常量值的谓词。例如,如果表仅按 id 列进行分段,请提供具有值 12234 的 id。您还可以为表中其他未分段的列指定附加谓词。谓词的作用类似于 SQL WHERE 子句,多个谓词/谓词表达式通过 SQL AND 修饰符一起应用。必须为谓词定义常量值。谓词表达式可用于细化查询,并且可以包含任意 SQL 表达式(例如,小于和大于等)以用于表中未分段的列。

Vertica JDBC 文档中提供了适用于 JDBC 驱动程序中所有类和方法的 Java 文档。

2.3.3.8.1 - 创建与可路由查询 API 一起使用的表和投影

对于可路由查询,客户端必须确定适用于获取数据的节点。客户端通常以下方法执行此操作:比较对表可用的所有投影,然后确定可用于查找包含数据的单个节点的最佳投影。您必须在至少一个表上创建按键列分段的投影,以充分利用可路由查询 API。联接到此表的其他表必须具有未分段投影,或者必须具有按下文所属进行分段的投影。

创建与可路由查询结合使用的表

若要创建可以与可路由查询 API 结合使用的表,请按均匀分布的列对表进行分段(使用哈希算法)。通常,可以按主键进行分段。为提高查找速度,请按用作分段依据的相同列对投影进行排序。例如,若要创建非常适用于可路由查询的表,请执行下列操作:


CREATE TABLE users (
id INT NOT NULL PRIMARY KEY,
username VARCHAR(32),
email VARCHAR(64),
business_unit VARCHAR(16))
ORDER BY id
SEGMENTED BY HASH(id)
ALL NODES;

此表基于 id 列进行分段(并按 id 进行排序以提高查找速度)。若要使用可路由查询 API 为此表构建查询,您只需要为 id 列提供单个谓词,当您查询该列时,将返回单个行。

不过,您可以向分段子句添加多个列。例如:

CREATE TABLE users2 (
    id INT NOT NULL PRIMARY KEY,
    username VARCHAR(32),
    email VARCHAR(64),
    business_unit VARCHAR(16))
ORDER BY id, business_unit
SEGMENTED BY HASH(id, business_unit)
ALL NODES;

在本例中,您需要在查询 users2 表时提供两个谓词,因为它按以下两列分段:idbusiness_unit。但是,如果您在执行查询时知道 idbusiness_unit,则同时按这两个列进行分段会有帮助,因为这样可使客户端更容易确定该投影是可用于确定正确节点的最佳投影。

针对单节点 JOIN 设计表

如果打算在可路由查询期间使用 VerticaRoutableExecutor 类并联接表,则您必须按同一个分段键对要联接的所有表进行分段。通常,此键是要联接的所有表上的主键/外键。例如,customer_key 可能是客户维度表中的主键,并且同一个键是销售事实数据表中的外键。使用这些表的 VerticaRoutableExecutor 查询的投影必须使用哈希算法按每个表中的客户键进行分段。

如果要与小型维度表(例如日期维度)联接,则使这些表保持未分段可能是合适做法,以使 date_dimension 数据存在于所有节点上。请务必注意,在联接未分段的表时,您仍必须在 createRoutableExecutor() 调用中指定已分段的表。

验证表的现有投影

如果已使用哈希算法对表进行分段(例如按 ID 列),则您可以通过使用 Vertica 函数 GET_PROJECTIONS 查看该表的投影,确定需要使用哪些谓词来查询该表。例如:

=> SELECT GET_PROJECTIONS ('users');
...
Projection Name: [Segmented] [Seg Cols] [# of Buddies] [Buddy Projections] [Safe] [UptoDate] [Stats]
----------------------------------------------------------------------------------------------------
public.users_b1 [Segmented: Yes] [Seg Cols: "public.users.id"] [K: 1] [public.users_b0] [Safe: Yes] [UptoDate: Yes] [Stats: RowCounts]
public.users_b0 [Segmented: Yes] [Seg Cols: "public.users.id"] [K: 1] [public.users_b1] [Safe: Yes] [UptoDate: Yes] [Stats: RowCounts]

对于每个投影,仅指定 public.users.id 列,表示您的查询谓词应包含此列。

如果表按多个列(例如 idbusiness_unit)进行分段,则您需要将这两个列作为谓词提供给可路由查询。

2.3.3.8.2 - 为可路由查询创建连接

JDBC 可路由查询 API 提供了 VerticaRoutableConnection 接口( JDBC 文档中提供了详细信息),此接口可用于连接到群集以及启用可路由查询。除了普通 VerticaConnection 所提供的功能之外,此接口还提供高级路由功能。VerticaRoutableConnection 提供对 VerticaRoutableExecutor 类和 VGet 类的访问。请分别参阅使用 VerticaRoutableExecutor 类为可路由查询定义查询使用 VGet 类为可路由查询定义查询

可以通过将 EnableRoutableQueries JDBC 连接属性设置为 true 来启用对此类的访问。

VerticaRoutableConnection 维护内部连接池和表元数据缓存(由连接的 createRoutableExecutor()/prepareGet() 方法所生成的所有 VerticaRoutableExecutor/VGet 对象共享)。此接口还是一个完全开发的独立 JDBC 连接,并且支持 VerticaConnection 所支持的所有功能。当此连接关闭时,由此 VerticaRoutableConnection 管理的所有池连接和所有子对象也会关闭。连接池和元数据仅由子可路由查询操作使用。

例如:

您可以使用 JDBC 数据源创建连接:


com.vertica.jdbc.DataSource jdbcSettings = new com.vertica.jdbc.DataSource();
jdbcSettings.setDatabase("exampleDB");
jdbcSettings.setHost("v_vmart_node0001.example.com");
jdbcSettings.setUserID("dbadmin");
jdbcSettings.setPassword("password");
jdbcSettings.setEnableRoutableQueries(true);
jdbcSettings.setPort((short) 5433);

VerticaRoutableConnection conn;
conn = (VerticaRoutableConnection)jdbcSettings.getConnection();

您还可以使用连接字符串和 DriverManager.getConnection() 方法创建连接:

String connectionString = "jdbc:vertica://v_vmart_node0001.example.com:5433/exampleDB?user=dbadmin&password=&EnableRoutableQueries=true";
VerticaRoutableConnection conn = (VerticaRoutableConnection) DriverManager.getConnection(connectionString);

以上两种方法生成相同的 conn 连接对象。

除了由可路由查询 API 添加到 Vertica JDBC 连接类的 setEnableRoutableQueries 属性之外,该 API 还添加了其他属性。完整列表如下。

  • EnableRoutableQueries:启用可路由查询查找功能。默认值为 false。

  • FailOnMultiNodePlans:如果计划需要多个节点,并且 FailOnMultiNodePlans 设置为 true,查询将失败。如果此属性设置为 false,则会生成警告,并且查询会继续执行。但是,延迟会显著提高,因为可路由查询必须先确定数据是否位于多个节点上,然后使用传统执行(在所有节点上)来运行普通查询。默认值为 true。请注意,仅使用谓词和常量值的简单调用不会出现此失败。

  • MetadataCacheLifetime:保留投影元数据的时间(以秒为单位)。API 缓存有关投影的元数据以用于查询(例如投影)。后续的查询使用该缓存来缩短响应时间。默认值为 300 秒。

  • MaxPooledConnections:保留在 VerticaRoutableConnection 内部池中的最大连接数(群集范围)。默认值 20。

  • MaxPooledConnectionsPerNode:保留在 VerticaRoutableConnection 内部池中的最大连接数(每节点)。默认值 5。

2.3.3.8.3 - 使用 VerticaRoutableExecutor 类为可路由查询定义查询

使用 VerticaRoutableExecutor 类从单个节点直接访问表数据。 VerticaRoutableExecutor 仅在包含查询所需的所有数据的节点上直接查询 Vertica,从而避免产生与 Vertica 查询执行关联的分布式规划和执行成本。您可以使用 VerticaRoutableExecutor 联接表或使用 GROUP BY 子句,因为使用 VGet 无法执行这些操作。

使用 VerticaRoutableExecutor 类时,适用以下规则:

  • 如果要联接表,您必须按联接谓词中引用的相同列集对要联接的所有表进行分段(使用哈希算法),除非要联接的表未分段。
  • 联接 WHERE 子句中的多个条件必须使用 AND 联接在一起。在 WHERE 子句中使用 OR 会导致查询退化成多节点计划。如果数据存在于同一节点上,则可以在联接条件 outside 列上指定 OR、IN 列表或范围条件。
  • 每个请求只能执行单个语句。不允许使用链接的 SQL 语句。
  • 在由驱动程序生成的子查询中使用您的查询有助于确定该查询是否可以在单个节点上执行。因此,不能在语句的结尾包含分号,也不能包含使用双破折号的 SQL 注释 (--),因为这些会导致由驱动程序生成的查询失败。

可以通过对连接对象调用 createRoutableExecutor 方法来创建 VerticaRoutableExecutor。

createRoutableExecutor( schema‑name, table‑name )

例如:


VerticaRoutableConnection conn;
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("customer_key", 1);
try {
    conn = (VerticaRoutableConnection)
        jdbcSettings.getConnection();
     String table = "customers";
     VerticaRoutableExecutor q = conn.createRoutableExecutor(null, table);
     ...
}...

如果 schema‑name 设置为空,则会使用搜索路径来查找表。

VerticaRoutableExecutor 方法

VerticaRoutableExecutor 包含以下方法:

  • [execute](#execute()

  • [关闭](#close())

  • getWarnings

有关此类的详细信息,请参阅 JDBC 文档。

执行

execute( query-string, { column, value | map } )

运行查询。

需要满足以下要求:

  • 要执行的查询必须使用符合 VerticaRoutableExecutor 类规则的常规 SQL。例如,假如数据存在于单个节点上,则您可以添加限制和排序或使用聚合函数。

  • JDBC 客户端使用/映射实参来确定应在哪个节点上执行查询。查询的内容必须使用您在列/值或映射实参中提供的相同值。

  • 不能使用以下数据类型作为列值: * INTERVAL * TIMETZ * TIMESTAMPTZ

    此外,如果表在具有以下数据类型的任何列上分段,则无法使用可路由查询 API 查询此表:

将查询发送到服务器之前,驱动程序不会验证查询的语法。如果表达式不正确,则查询会失败。

关闭

close()

通过释放由此 VerticaRoutableExecutor 使用的资源来关闭此 VerticaRoutableExecutor。它不会关闭与 Vertica 的父 JDBC 连接。

getWarnings

getWarnings()

检索对此 VerticaRoutableExecutor 的调用所报告的第一个警告。其他警告均为链式,可使用 JDBC 方法 getNextWarning() 访问。

示例

以下示例显示如何使用 VerticaRoutableExecutor 执行同时使用 JOIN 子句和带有 GROUP BY 子句的聚合函数的查询。此示例还显示如何创建客户表和销售表,并对这些表进行分段,以便可以使用 VerticaRoutableExecutor 类联接这些表。此示例使用 VMart 架构中的 date_dimension 表来显示如何联接未分段的表上的数据。

  1. 创建 customers 表以存储客户详细信息,然后创建在表的 customer_key 列上分段的投影:

    
    => CREATE TABLE customers (customer_key INT, customer_name VARCHAR(128), customer_email VARCHAR(128));
    => CREATE PROJECTION cust_proj_b0 AS SELECT * FROM customers SEGMENTED BY HASH (customer_key) ALL NODES;
    => CREATE PROJECTION cust_proj_b1 AS SELECT * FROM customers SEGMENTED BY HASH (customer_key) ALL NODES OFFSET 1;
    => CREATE PROJECTION cust_proj_b2 AS SELECT * FROM customers SEGMENTED BY HASH (customer_key) ALL NODES OFFSET 2;
    => SELECT start_refresh();
    
  2. 创建 sales 表,然后创建在其 customer_key 列上分段的投影。由于 customersales 表按同一个键进行分段,因此您稍后可以使用 VerticaRoutableExecutor 可路由查询查找来联接这两个表。

    
    => CREATE TABLE sales (sale_key INT, customer_key INT, date_key INT, sales_amount FLOAT);
    => CREATE PROJECTION sales_proj_b0 AS SELECT * FROM sales SEGMENTED BY HASH (customer_key) ALL NODES;
    => CREATE PROJECTION sales_proj_b1 AS SELECT * FROM sales SEGMENTED BY HASH (customer_key) ALL NODES OFFSET 1;
    => CREATE PROJECTION sales_proj_b2 AS SELECT * FROM sales SEGMENTED BY HASH (customer_key) ALL NODES OFFSET 2;
    => SELECT start_refresh();
    
  3. 添加一些示例数据:

    => INSERT INTO customers VALUES (1, 'Fred', 'fred@example.com');
    => INSERT INTO customers VALUES (2, 'Sue', 'Sue@example.com');
    => INSERT INTO customers VALUES (3, 'Dave', 'Dave@example.com');
    => INSERT INTO customers VALUES (4, 'Ann', 'Ann@example.com');
    => INSERT INTO customers VALUES (5, 'Jamie', 'Jamie@example.com');
    => COMMIT;
    
    => INSERT INTO sales VALUES(1, 1, 1, '100.00');
    => INSERT INTO sales VALUES(2, 2, 2, '200.00');
    => INSERT INTO sales VALUES(3, 3, 3, '300.00');
    => INSERT INTO sales VALUES(4, 4, 4, '400.00');
    => INSERT INTO sales VALUES(5, 5, 5, '400.00');
    => INSERT INTO sales VALUES(6, 1, 15, '500.00');
    => INSERT INTO sales VALUES(7, 1, 15, '400.00');
    => INSERT INTO sales VALUES(8, 1, 35, '300.00');
    => INSERT INTO sales VALUES(9, 1, 35, '200.00');
    => COMMIT;
    
  4. 创建 VMart date_dimension 表的未分段投影以在此示例中使用。调用元函数 START_REFRESH 来取消现有数据的分段:

    => CREATE PROJECTION date_dim AS SELECT * FROM date_dimension UNSEGMENTED ALL NODES;
    => SELECT start_refresh();
    

现在,您可以使用 customersalesdate_dimension 数据创建如下可路由查询查找:使用联接和 group by 子句查询客户表并返回给定客户每天的总购买量:


import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import com.vertica.jdbc.kv.*;

public class verticaKV_doc {
    public static void main(String[] args) {
        com.vertica.jdbc.DataSource jdbcSettings
            = new com.vertica.jdbc.DataSource();
        jdbcSettings.setDatabase("VMart");
        jdbcSettings.setHost("vertica.example.com");
        jdbcSettings.setUserID("dbadmin");
        jdbcSettings.setPassword("password");
        jdbcSettings.setEnableRoutableQueries(true);
        jdbcSettings.setFailOnMultiNodePlans(true);
        jdbcSettings.setPort((short) 5433);
        VerticaRoutableConnection conn;
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("customer_key", 1);
        try {
            conn = (VerticaRoutableConnection)
                jdbcSettings.getConnection();
            String table = "customers";
            VerticaRoutableExecutor q = conn.createRoutableExecutor(null, table);
            String query = "select d.date, SUM(s.sales_amount) as Total ";
                query += " from customers as c";
                query += " join sales as s ";
                query += " on s.customer_key = c.customer_key ";
                query += " join date_dimension as d ";
                query += " on d.date_key = s.date_key ";
                query += " where c.customer_key = " + map.get("customer_key");
                query += " group by (d.date) order by Total DESC";
            ResultSet rs = q.execute(query, map);
            while(rs.next()) {
                System.out.print("Date: " + rs.getString("date") + ":  ");
                System.out.println("Amount: " + rs.getString("Total"));
            }
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

示例代码生成如下输出:

Date: 2012-01-15:  Amount: 900.0
Date: 2012-02-04:  Amount: 500.0
Date: 2012-01-01:  Amount: 100.0

2.3.3.8.4 - 使用 VGet 类为可路由查询定义查询

如果不需要联接表或使用 group by 子句,您可以使用 VGet 类从单个节点直接访问表数据。与 VerticaRoutableExecutor 一样,VGet 直接查询包含查询所需的数据的 Vertica 节点,从而避免产生与正常的 Vertica 执行关联的分布式规划和执行成本。但是,VGet 不使用 SQL。相反,您可以定义谓词和值以对单个表执行键/值类型查找。VGet 非常适用于对单个表执行键/值类型查找。

可以通过对连接对象调用 prepareGet 方法来创建 VGet。

prepareGet( schema‑name, { table‑name | projection‑name } )

例如:


VerticaRoutableConnection conn;
try {
    conn = (VerticaRoutableConnection)
        jdbcSettings.getConnection();
        System.out.println("Connected.");
        VGet get = conn.prepareGet("public", "users");
              ...
}...

VGet 操作跨越多个 JDBC 连接(和多个 Vertica 会话),并且不遵循父连接的事务语义。如果需要在多个执行之间保持一致性,您可以使用父 VerticaRoutableConnection 的一致读取 API 来保证所有操作在同一时期进行。

VGet 是线程安全的,但会同步所有方法,因此共享 VGet 实例的线程永远不会并行运行。为了提高并行度,每个线程应具有各自的 VGet 实例。对同一个表执行操作的不同 VGet 实例共享池连接和元数据,以便实现较高的并行度。

VGet 方法

VGet 包含以下方法:

默认情况下,VGet 提取可满足多个谓词的逻辑 AND 的所有行的所有列,这些谓词通过 addPredicate 方法传递。您可以使用以下方法进一步自定义 get 操作:addOutputColumnaddOutputExpressionaddPredicateExpressionaddSortColumnsetLimit

addPredicate

addPredicate(string, object)

向查询添加谓词列和常量值。必须为用作表的分段依据的每个列包含一个谓词。谓词充当查询 WHERE 子句。多个 addPredicate 方法调用由 AND 修饰符联接。每次调用并执行后,VGet 都会保留此值。要移除它,请使用 clearPredicates

不能使用以下数据类型作为列值。同样,如果表在具有以下数据类型的任何列上分段,则不能使用可路由查询 API 查询此表:

addPredicateExpression

addPredicateExpression(string)

接受对表列执行操作的任意 SQL 表达式作为查询的输入。谓词表达式和谓词由 AND 修饰符联接。可以在谓词表达式中使用已分段的列,但您必须同时使用 addPredicate 将这些谓词表达式指定为常规谓词。每次调用并执行后,VGet 都会保留此值。要移除它,请使用 clearPredicates

驱动程序将表达式发送至服务器之前不会验证表达式的语法。如果表达式不正确,则查询会失败。

addOutputColumn

addOutputColumn(string)

添加要包含到输出的列。默认情况下,查询作为 SELECT * 运行,并且您不需要定义任何用于返回数据的输出列。如果要添加输出列,则您必须添加要返回的所有列。每次调用并执行后,VGet 都会保留此值。要移除它,请使用 clearOutputs

addOutputExpression

addOutputExpression(string)

接受对表列执行操作的任意 SQL 表达式作为输出。每次调用并执行后,VGet 都会保留此值。要移除它,请使用 ClearOutputs。

存在以下限制:

  • 驱动程序将表达式发送至服务器之前不会验证表达式的语法。如果表达式不正确,则查询会失败。

  • addOutputExpression 在查询 Flex 表时不受支持。如果尝试对 Flex 表查询使用 addOutputExpression,则会引发 SQLFeatureNotSupportedException

addSortColumn

addSortColumn(string, SortOrder)

向输出列添加排序顺序。输出列既可以是默认查询 (SELECT *) 所返回的一个列,也可以是 addSortColumn 或 addOutputExpress 中定义的列之一。可以定义多个排序列。

setLimit

setLimit(int)

对返回的结果数设置限制。值为 0 的限制表示无限制。

clearPredicates

clearPredicates()

移除由 addPredicateaddPredicateExpression 添加的谓词。

clearOutputs

clearOutputs()

移除由 addOutputColumnaddOutputExpression 添加的输出。

clearSortColumns

clearSortColumns()

移除先前由 addSortColumn 添加的排序列。

执行

execute()

运行查询。必须格外小心,以确保谓词列存在于 VGet 所使用的表和投影上,并确保表达式不需要在多个节点上执行。如果表达式由于十分复杂而需要在多个节点上执行,execute 将在 FailOnMultiNodePlans 连接属性设置为 true 时引发 SQLException。

关闭

close()

通过释放由此 VGet 使用的资源来关闭此 VGet。它不会关闭与 Vertica 的父 JDBC 连接。

getWarnings

getWarnings()

检索对此 VGet 的调用所报告的第一个警告。其他警告均为链式,可使用 JDBC 方法 getNextWarning 进行访问。

示例

以下代码查询在创建与可路由查询 API 一起使用的表和投影中定义的 users 表。该表定义了一个使用哈希算法进行分段的 id 列。

import java.sql.*;
import com.vertica.jdbc.kv.*;

public class verticaKV2 {
    public static void main(String[] args) {
        com.vertica.jdbc.DataSource jdbcSettings
            = new com.vertica.jdbc.DataSource();
        jdbcSettings.setDatabase("exampleDB");
        jdbcSettings.setHost("v_vmart_node0001.example.com");
        jdbcSettings.setUserID("dbadmin");
        jdbcSettings.setPassword("password");
        jdbcSettings.setEnableRoutableQueries(true);
        jdbcSettings.setPort((short) 5433);

        VerticaRoutableConnection conn;
        try {
            conn = (VerticaRoutableConnection)
                jdbcSettings.getConnection();
                System.out.println("Connected.");
            VGet get = conn.prepareGet("public", "users");
            get.addPredicate("id", 5);
            ResultSet rs = get.execute();
            rs.next();
            System.out.println("ID: " +
                rs.getString("id"));
            System.out.println("Username: "
                + rs.getString("username"));
            System.out.println("Email: "
                + rs.getString("email"));
            System.out.println("Closing Connection.");
            conn.close();
        } catch (SQLException e) {
            System.out.println("Error! Stacktrace:");
            e.printStackTrace();
        }
    }
}

该代码将生成以下输出:

Connected.
ID: 5
Username: userE
Email: usere@example.com
Closing Connection.

2.3.3.8.5 - 可路由查询性能和故障排除

本主题详细介绍性能注意事项和使用可路由查询 API 时可能会遇到的常见问题。

将资源池与可路由查询结合使用

各个可路由查询将快速得到处理,因为它们直接访问单个节点并返回一个或几个数据行。但在默认情况下,Vertica 资源池对 execution parallelism 参数使用 AUTO 设置。如果将此参数设置为 AUTO,则设置由可用的 CPU 核心数决定,并通常导致对资源池中的查询进行多线程执行。在服务器上创建并行线程会降低效率,因为可路由查询操作返回数据的速度太快,并且可路由查询操作仅使用单个线程查找行。若要防止服务器打开不需要的处理线程,应为可路由查询客户端创特定的资源池。请考虑用于可路由查询的资源池的以下设置:

  • 将执行并行度设置为 1,以强制执行单线程查询。此设置能够提高可路由查询的性能。

  • 使用 CPU 相关性将资源池限制为某个特定 CPU 或 CPU 集。此设置不但能够确保可路由查询具有可用资源,而且能够防止可路由查询对其他常规查询的系统性能造成显著影响。

  • 如果不为资源池设置 CPU 相关性,可以考虑将资源池的最大并发值设置为既能确保可路由查询性能良好又不会对常规查询的性能造成负面影响的设置。

可路由查询连接的性能注意事项

由于 VerticaRoutableConnection 将打开内部连接池,因此必须根据群集大小和同时客户端连接的数量适当地配置 MaxPooledConnectionsMaxPooledConnectionsPerNode。如果要使用 VerticaRoutableConnection 重载群集,则您无法影响正常的数据库连接。

与发起程序节点的初始连接可发现群集中的所有其他节点。发送 VerticaRoutableExecutor 或 VGet 查询之前,不会打开内部池连接。连接对象中的所有 VerticaRoutableExecutor/VGet 均使用来自内部池的连接,并受到 MaxPooledConnections 设置的限制。连接将保持打开,直至在已达到连接限制时关闭这些连接以便在其他位置打开新连接为止。

可路由查询故障排除

可路由查询问题通常分为以下两个类别:

  • 提供的谓词数量不足够。

  • 查询必须跨越多个节点。

谓词要求

必须提供与表(使用哈希算法进行分段)的列数相同的谓词数量。要确定分段列,请调用 Vertica 函数 GET_PROJECTIONS。必须为 Seg Cols 字段中显示的每个列提供一个谓词。

对于 VGet,这意味着您必须使用 addPredicate() 以添加每个列。对于 VerticaRoutableExecutor,这意味着您必须在发送到 execute() 的映射中提供所有谓词和值。

多节点故障

无法定义正确数量的谓词,但仍会发生故障,因为数据包含在多个节点上。发生此故障的原因是投影的数据未以某种方式进行分段,以使要查询的数据包含在单个节点上。请为连接启用日志记录并查看日志,以验证所使用的投影。如果客户端未选择正确的投影,则会通过在 create/prepare 语句中指定投影而非表来尝试直接查询投影,例如:

  • 使用 VerticaRoutableExecutor:

    conn.createRoutableExecutor(schema, table/projection);
    
  • 使用 VGet:

    conn.prepareGet('schema','table/projection')
    

此外,在 vsql 中使用 EXPLAIN 命令有助于确定查询是否可以在单个节点中运行。EXPLAIN 可以帮助了解为什么查询在单个或多个节点中运行。

2.3.3.8.6 - 使用 VHash 对数据进行预分段

VHash 类是 Vertica 哈希函数的实施,可与 JDBC 客户端应用程序结合使用。

使用 Vertica 中的哈希分段,您可以基于内置的哈希函数对投影进行分段。内置的哈希函数可使数据平均分布到群集中的部分或全部节点,从而提供最佳的查询执行。

假设您有数百万个值行分布在几千个 CSV 文件之中。假设您已创建了一个使用哈希算法进行分段的表。将值加载到数据库之前,您可能想要确定应将特定值加载到哪个节点。因此,使用 VHash 会特别有帮助,因为此类允许您在加载数据之前对数据进行预分段。

以下示例显示了对名为“testFile.csv”的文件的第一列使用哈希算法的 VHash 类。该文件中第一列的名称为 meterId

使用 VHash 对数据进行分段

以下示例演示了如何从本地文件系统读取 testFile.csv 文件并对 meteterId 列运行哈希函数。然后,您可以使用投影中的数据库元数据基于 meterId 的哈希值对该文件中的各个列进行预分段。

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.io.IOException;
import java.sql.*;

import com.vertica.jdbc.kv.VHash;

public class VerticaKVDoc {

    final Map<String, FileOutputStream> files;
    final Map<String, List<Long>> nodeToHashList;
    String segmentationMetadata;
    List<String> lines;

    public static void main(String[] args) throws Exception {
        try {
            Class.forName("com.vertica.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.err.println("Could not find the JDBC driver class.");
            e.printStackTrace();
            return;
        }

        Properties myProp = new Properties();
        myProp.put("user", "username");
        myProp.put("password", "password");

        VerticaKVDoc ex = new VerticaKVDoc();

        // Read in the data from a CSV file.
        ex.readLinesFromFile("C:\\testFile.csv");

        try (Connection conn = DriverManager.getConnection(
                "jdbc:vertica://VerticaHost:portNumber/databaseName", myProp)) {

        // Compute the hashes and create FileOutputStreams.
        ex.prepareForHashing(conn);

        }

        // Write to files.
        ex.writeLinesToFiles();
    }

    public VerticaKVDoc() {
        files = new HashMap<String, FileOutputStream>();
        nodeToHashList = new HashMap<String, List<Long>>();
    }

    public void prepareForHashing(Connection conn) throws SQLException,
            FileNotFoundException {

        // Send a query to Vertica to return the projection segments.
        try (ResultSet rs = conn.createStatement().executeQuery(
                "SELECT get_projection_segments('public.projectionName')")) {
            rs.next();
            segmentationMetadata = rs.getString(1);
        }

        // Initialize the data files.
        try (ResultSet rs = conn.createStatement().executeQuery(
                "SELECT node_name FROM nodes")) {
            while (rs.next()) {
                String node = rs.getString(1);
                files.put(node, new FileOutputStream(node + ".csv"));
            }
        }
    }

    public void writeLinesToFiles() throws UnsupportedEncodingException,
            IOException {
        for (String line : lines) {

            long hashedValue = VHash.hashLong(getMeterIdFromLine(line));

            // Write the row data to that node's data file.
            String node = VHash.getNodeFor(segmentationMetadata, hashedValue);

            FileOutputStream fos = files.get(node);
            fos.write(line.getBytes("UTF-8"));
        }
    }

    private long getMeterIdFromLine(String line) {

        // In our file, "meterId" is the name of the first column in the file.
        return Long.parseLong(line.split(",")[0]);
    }

    public void readLinesFromFile(String filename) throws IOException {
        lines = new ArrayList<String>();
        String line;
        try (BufferedReader reader = new BufferedReader(
                new FileReader(filename))) {
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        }
    }

}

2.3.4 - JavaScript

开源 vertica-nodejs 客户端驱动程序允许您使用 JavaScript 与数据库交互。有关详细信息,请参阅 npm 上的 vertica-nodejs 包

2.3.5 - Perl

Perl 脚本可以通过使用 Perl DBI 模块和 DBD::ODBC 数据库驱动程序连接到 Vertica 的 ODBC 驱动程序接口来与 Vertica 交互。

先决条件

在创建 Perl 客户端应用程序之前,必须配置 Perl 开发环境

2.3.5.1 - 配置 Perl 开发环境

Perl 包含一个数据库接口 (DBI) 模块,此接口模块可为 Perl 脚本创建与数据库交互的标准接口。此接口模块依赖数据库驱动程序 (Database Driver, DBD) 模块处理所有特定于数据库的通信任务。结果是一个接口提供了一种一致的方式以供 Perl 脚本用来与许多不同类型的数据库交互。

Perl 脚本可以通过使用 Perl DBI 模块和 DBD::ODBC 数据库驱动程序连接到 Vertica 的 ODBC 驱动程序接口来与 Vertica 交互。有关详细文档,请参阅 Perl 的 DBI 模块和 DBD::ODBC 模块的 CPAN 页面。

Vertica-Perl 架构

Perl 开发环境依赖于 Vertica ODBC 驱动程序以及 DBI 和 DBD::ODBC 模块。

  1. 安装和配置 ODBC

  2. 使用以下命令验证是否安装了 Perl。如果此命令不返回版本信息,您必须安装 Perl。有关版本支持,请参阅 Perl 驱动程序要求

    $ perl -v
    
  3. 安装 Perl 模块 DBIDBD::ODBC兼容版本。安装方法因环境不同而异。有关安装 Perl 模块的详细信息,请参阅 cpan 文档

  4. 运行以下命令以验证是否已安装 DBI 和 DBD::ODBC。如果已安装,这些命令不应返回任何内容。否则,它们会返回错误:

    $ perl -e "use DBI;"
    $ perl -e "use DBD::ODBC;"
    

列出 DSN 并验证安装

验证安装的另一种方法是使用以下 Perl 脚本。此脚本验证是否已安装 DBI 和 DBD::ODBC 并输出您的 ODBC DSN(如果有):

#!/usr/bin/perl
use strict;
# Attempt to load the DBI module in an eval using require. Prevents
# script from erroring out if DBI is not installed.
eval
{
    require DBI;
    DBI->import();
};
if ($@) {
    # The eval failed, so DBI must not be installed
    print "DBI module is not installed\n";
} else {
    # Eval was successful, so DBI is installed
    print "DBI Module is installed\n";
    # List the drivers that DBI knows about.
    my @drivers = DBI->available_drivers;
    print "Available Drivers: \n";
    foreach my $driver (@drivers) {
        print "\t$driver\n";
    }
    # See if DBD::ODBC is installed by searching driver array.
    if (grep {/ODBC/i} @drivers) {
        print "\nDBD::ODBC is installed.\n";
        # List the ODBC data sources (DSNs) defined on the system
        print "Defined ODBC Data Sources:\n";
        my @dsns = DBI->data_sources('ODBC');
        foreach my $dsn (@dsns) {
            print "\t$dsn\n";
        }
    } else {
        print "DBD::ODBC is not installed\n";
    }
}

如果您的系统配置正确,则输出应类似如下内容:

DBI Module is installed
Available Drivers:
        ADO
        DBM
        ExampleP
        File
        Gofer
        ODBC
        Pg
        Proxy
        SQLite
        Sponge
        mysql
DBD::ODBC is installed.
Defined ODBC Data Sources:
        dbi:ODBC:dBASE Files
        dbi:ODBC:Excel Files
        dbi:ODBC:MS Access Database
        dbi:ODBC:VerticaDSN

2.3.5.2 - 使用 Perl 连接到 Vertica

可以使用 Perl DBI 模块的 connect 函数连接到 Vertica。此函数使用必需的数据源字符串参数,以及用于用户名、密码和连接属性的可选参数。

数据源字符串必须以 "dbi:ODBC:" 开头,以指示 DBI 模块使用 DBD::ODBC 驱动程序来连接到 Vertica。该字符串的剩余部分由 DBD::ODBC 驱动程序解释。该字符串通常包含 DSN 的名称(包含连接到 Vertica 数据库所需的连接信息)。例如,若要指示 DBD::ODBC 驱动程序使用名为 VerticaDSN 的 DSN,您应使用如下所示的数据源字符串:

"dbi:ODBC:VerticaDSN"

用户名参数和密码参数是可选的。但是,如果不提供这两个参数(或者仅提供免密码帐户的用户名)并且未在 DSN 中设置这两个参数,则连接尝试将始终失败。

connect 函数将在连接到 Vertica 后返回数据库句柄。如果未连接,则会返回 undef。在这种情况下,您可以访问 DBI 模块的错误字符串属性 ($DBI::errstr) 以获取错误消息。

以下示例演示了使用名为 VerticaDSN 的 DSN 连接到 Vertica。对 connect 的调用提供了用户名和密码。连接后,此示例将调用数据库句柄的 disconnect 函数以关闭连接:

#!/usr/bin/perl -w
use strict;
use DBI;
# Open a connection using a DSN. Supply the username and password.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123");
unless (defined $dbh) {
    # Conection failed.
    die "Failed to connect: $DBI::errstr";
}
print "Connected!\n";
$dbh->disconnect();

2.3.5.2.1 - 在 Perl 中设置 ODBC 连接参数

若要设置 ODBC 连接参数,请在源数据字符串中将 DSN 名称替换为参数名称/值对的分号分隔列表。可以使用 DSN 参数向 DBD::ODBC 说明应使用哪个 DSN,然后添加要设置的其他 ODBC 参数。例如,以下代码使用名为 VerticaDSN 的 DSN 进行连接并将该连接的区域设置设置为 en_GB。

#!/usr/bin/perl -w
use strict;
use DBI;
# Instead of just using the DSN name, use name and value pairs.
my $dbh = DBI->connect("dbi:ODBC:DSN=VerticaDSN;Locale=en_GB@collation=binary","ExampleUser","password123");
unless (defined $dbh) {
    # Conection failed.
    die "Failed to connect: $DBI::errstr";
}
print "Connected!\n";
$dbh->disconnect();

有关可在源数据字符串中设置的连接参数的列表,请参阅 ODBC DSN 连接属性

2.3.5.2.2 - 设置 Perl DBI 连接属性

Perl DBI 模块包含一些属性,您可以使用这些属性控制其数据库连接的行为。这些属性与 ODBC 连接参数相似(在某些情况下,它们复制彼此的功能)。DBI 连接属性是一种用于控制数据库连接的行为的跨平台方法。

可以在建立连接时通过向 DBI connect 函数传递包含属性/值对的哈希值来设置 DBI 连接属性。例如,若要将 DBI 连接属性 AutoCommit 设置为 false,您应使用以下语句:

# Create a hash that holds attributes for the connection
my $attr = {AutoCommit => 0};
# Open a connection using a DSN. Supply the username and password.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123",
    $attr);

有关可在数据库连接上设置的属性的完整描述,请参阅 DBI 文档的 Database Handle Attributes(“数据库句柄属性”)一节。

脚本已连接之后,它可以通过将数据库句柄用作哈希引用来访问和修改连接属性。例如:

print "The AutoCommit attribute is: " . $dbh->{AutoCommit} . "\n";

以下示例演示了设置两个连接属性:

  • RaiseError 控制 DBI 驱动程序是否在遇到数据库错误时生成 Perl 错误。通常,如果希望 Perl 脚本在遇到数据库错误时退出,您可以将此属性设置为 true (1)。

  • AutoCommit 控制语句是否在完成后自动提交其事务。DBI 默认设置为 Vertica 的 AutoCommit 默认值 (true)。批量加载数据时,始终应将 AutoCommit 设置为 false (0) 以提高数据库效率。

#!/usr/bin/perl
use strict;
use DBI;
# Create a hash that holds attributes for the connection
 my $attr = {
                RaiseError => 1, # Make database errors fatal to script
                AutoCommit => 0, # Prevent statements from committing
                                 # their transactions.
            };
# Open a connection using a DSN. Supply the username and password.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123",
    $attr);

if (defined $dbh->err) {
    # Connection failed.
    die "Failed to connect: $DBI::errstr";
}
print "Connected!\n";
# The database handle lets you access the connection attributes directly:
print "The AutoCommit attribute is: " . $dbh->{AutoCommit} . "\n";
print "The RaiseError attribute is: " . $dbh->{RaiseError} . "\n";
# And you can change values, too...
$dbh->{AutoCommit} = 1;
print "The AutoCommit attribute is now: " . $dbh->{AutoCommit} . "\n";
$dbh->disconnect();

运行该示例后,将输出以下内容:

Connected!The AutoCommit attribute is: 0
The RaiseError attribute is: 1
The AutoCommit attribute is now: 1

2.3.5.2.3 - 不使用 DSN 从 Perl 进行连接

如果不想为数据库设置数据源名称 (DSN),您可以在数据源字符串中提供 Perl 的 DBD::ODBC 驱动程序连接到 Vertica 数据库所需的所有信息。此源字符串必须包含 DRIVER= 参数,以向 DBD::ODBC 说明应使用哪个驱动程序库进行连接。此参数的值是客户端系统的驱动程序管理器分配给驱动程序的名称。

  • 在 Windows 上,驱动程序管理器分配给 Vertica ODBC 驱动程序的名称是 Vertica。

  • 在 Linux 和其他类 UNIX 操作系统上,Vertica ODBC 驱动程序的名称在系统的 odbcinst.ini 文件中分配。例如,如果 /etc/odbcint.ini 包含以下内容:

    [Vertica]
    Description = Vertica ODBC Driver
    Driver = /opt/vertica/lib64/libverticaodbc.so
    

    您应使用名称 Vertica。有关 odbcinst.ini 文件的详细信息,请参阅为 Linux 创建 ODBC DSN

可以在字符串中利用 Perl 的变量扩展,以将变量用于大部分连接属性,如以下示例所示。

#!/usr/bin/perl
use strict;
use DBI;
my $server='VerticaHost';
my $port = '5433';
my $database = 'VMart';
my $user = 'ExampleUser';
my $password = 'password123';
# Connect without a DSN by supplying all of the information for the connection.
# The DRIVER value on UNIX platforms depends on the entry in the odbcinst.ini
# file.
my $dbh = DBI->connect("dbi:ODBC:DRIVER={Vertica};Server=$server;" .
        "Port=$port;Database=$database;UID=$user;PWD=$password")
        or die "Could not connect to database: " . DBI::errstr;
print "Connected!\n";
$dbh->disconnect();

2.3.5.3 - 使用 Perl 执行语句

Perl 脚本已连接到 Vertica(请参阅使用 Perl 连接到 Vertica)之后,它可以使用 Perl DBI 模块的 do 函数执行将返回值而非结果集的简单语句。通常可以使用此函数执行 DDL 语句或诸如 COPY 等数据加载语句(请参阅在 Perl 中使用 COPY LOCAL 加载数据)。

#!/usr/bin/perl
use strict;
use DBI;
# Disable autocommit
 my $attr = {AutoCommit => 0};
# Open a connection using a DSN.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123",
    $attr);
unless (defined $dbh) {
    # Conection failed.
    die "Failed to connect: $DBI::errstr";
}
# You can use the do function to perform DDL commands.
# Drop any existing table.
$dbh->do("DROP TABLE IF EXISTS TEST CASCADE;");
# Create a table to hold data.
$dbh->do("CREATE TABLE TEST( \
               C_ID  INT, \
               C_FP  FLOAT,\
               C_VARCHAR VARCHAR(100),\
               C_DATE DATE, C_TIME TIME,\
               C_TS TIMESTAMP,\
               C_BOOL BOOL)");
# Commit changes and exit.
$dbh->commit();
$dbh->disconnect();

2.3.5.4 - 使用 Perl 批量加载数据

要使用 Perl 将大批量数据加载到 Vertica:

  1. 将 DBI 的 AutoCommit 连接属性设置为 false,以提高批量加载速度。有关禁用 AutoCommit 的示例,请参阅设置 Perl DBI 连接属性

  2. 调用数据库句柄的 prepare 函数以准备 SQL INSERT 语句(包含要插入的数据值的占位符)。例如:

    # Prepare an INSERT statement for the test table
    $sth = $dbh->prepare("INSERT into test values(?,?,?,?,?,?,?)");
    

    prepare 函数将返回一个语句句柄,您可以使用此句柄来插入数据。

  3. 为占位符分配数据。有几种方法可用于执行此操作。最简单的方法是使用 INSERT 语句中每个占位符的值来填充数组。

  4. 调用该语句句柄的 execute 函数以将数据行插入到 Vertica。此函数调用的返回值指示 Vertica 已接受还是已拒绝该行。

  5. 重复步骤 3 和 4,直至已加载所需的所有数据为止。

  6. 调用数据库句柄的 commit 函数以提交已插入的数据。

以下示例演示了通过以下方法插入小批量数据:使用数据填充一系列数组,然后在其中循环并插入每个行。

#!/usr/bin/perl
use strict;
use DBI;
# Create a hash reference that holds a hash of parameters for the
# connection.
 my $attr = {AutoCommit => 0, # Turn off autocommit
             PrintError => 0   # Turn off automatic error printing.
                               # This is handled manually.
             };
# Open a connection using a DSN. Supply the username and password.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123",
    $attr);
if (defined DBI::err) {
    # Conection failed.
    die "Failed to connect: $DBI::errstr";
}
print "Connection AutoCommit state is: " . $dbh->{AutoCommit} . "\n";
# Create table to hold inserted data
$dbh->do("DROP TABLE IF EXISTS TEST CASCADE;") or die "Could not drop table";
$dbh->do("CREATE TABLE TEST( \
               C_ID  INT, \
               C_FP  FLOAT,\
               C_VARCHAR VARCHAR(100),\
               C_DATE DATE, C_TIME TIME,\
               C_TS TIMESTAMP,\
               C_BOOL BOOL)") or die "Could not create table";
# Populate an array of arrays with values. One of these rows contains
# data that will not be sucessfully inserted. Another contains an
# undef value, which gets inserted into the database as a NULL.
my @data = (
                [1,1.111,'Hello World!','2001-01-01','01:01:01'
                    ,'2001-01-01 01:01:01','t'],
                [2,2.22222,'How are you?','2002-02-02','02:02:02'
                    ,'2002-02-02 02:02:02','f'],
                ['bad value',2.22222,'How are you?','2002-02-02','02:02:02'
                    ,'2002-02-02 02:02:02','f'],
                [4,4.22222,undef,'2002-02-02','02:02:02'
                    ,'2002-02-02 02:02:02','f'],
             );
# Create a prepared statement to use parameters for inserting values.
my $sth = $dbh->prepare_cached("INSERT into test values(?,?,?,?,?,?,?)");
my $rowcount = 0; # Count # of rows
# Loop through the arrays to insert values
foreach my $tuple (@data) {
    $rowcount++;
    # Insert the row
    my $retval = $sth->execute(@$tuple);

    # See if the row was successfully inserted.
    if ($retval == 1) {
        # Value of 1 means the row was inserted (1 row was affected by insert)
        print "Row $rowcount successfully inserted\n";
    } else {
        print "Inserting row $rowcount failed";
        # Error message is not set on some platforms/versions of DBUI. Check to
        # ensure a message exists to avoid getting an unitialized var warning.
        if ($sth->err()) {
                print ": " . $sth->errstr();
        }
        print "\n";
    }
}
# Commit changes. With AutoCommit off, you need to use commit for batched
# data to actually be committed into the database. If your Perl script exits
# without committing its data, Vertica rolls back the transaction and the
# data is not committed.
$dbh->commit();
$dbh->disconnect();

成功运行以上示例后,将显示以下内容:

Connection AutoCommit state is: 0
Row 1 successfully inserted
Row 2 successfully inserted
Inserting row 3 failed with error 01000 [Vertica][VerticaDSII] (20) An
error occurred during query execution: Row rejected by server; see
server log for details (SQL-01000)
Row 4 successfully inserted

请注意,未插入其中一个行,因为该行包含无法存储在整数列中的字符串值。有关与 Vertica 通信的 Perl 脚本中数据类型处理的详细信息,请参阅 Perl 数据类型和 Vertica 数据类型之间的转换

2.3.5.5 - 在 Perl 中使用 COPY LOCAL 加载数据

您可以使用 COPY LOCAL 将客户端系统上的分隔文件(例如具有逗号分隔值的文件)加载到 Vertica 中。COPY LOCAL 不是使用 Perl 读取、解析并批量插入文件数据,而是直接将文件数据从本地文件系统加载到 Vertica 中。执行完成后,COPY LOCAL 返回它成功插入的行数。

以下示例使用 COPY LOCAL 加载到 Vertica 本地文件 data.txt 中,该文件与 Perl 文件位于同一目录中。

#!/usr/bin/perl
use strict;
use DBI;
# Filesystem path handling module
use File::Spec;
# Create a hash reference that holds a hash of parameters for the
# connection.
 my $attr = {AutoCommit => 0}; # Turn off AutoCommit
# Open a connection using a DSN. Supply the username and password.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123",
    $attr) or die "Failed to connect: $DBI::errstr";
print "Connected!\n";
# Drop any existing table.
$dbh->do("DROP TABLE IF EXISTS Customers CASCADE;");
# Create a table to hold data.
$dbh->do("CREATE TABLE Customers( \
               ID  INT, \
               FirstName  VARCHAR(100),\
               LastName   VARCHAR(100),\
               Email      VARCHAR(100),\
               Birthday   DATE)");
# Find the absolute path to the data file located in the current working
# directory and named data.txt
my $currDir = File::Spec->rel2abs(File::Spec->curdir());
my $dataFile = File::Spec->catfile($currDir, 'data.txt');
print "Loading file $dataFile\n";
# Load local file using copy local. Return value is the # of rows affected
# which equates to the number of rows inserted.
my $rows = $dbh->do("COPY Customers FROM LOCAL '$dataFile' DIRECT")
     or die $dbh->errstr;
print "Copied $rows rows into database.\n";
$dbh->commit();
# Prepare a query to get the first 15 rows of the results
my $sth = $dbh->prepare("SELECT * FROM Customers WHERE ID < 15 \
                                 ORDER BY ID");

$sth->execute() or die "Error querying table: " . $dbh->errstr;
my @row; # Pre-declare variable to hold result row used in format statement.
# Use Perl formats to pretty print the output. Declare the heading for the
# form.
format STDOUT_TOP =
ID  First           Last          EMail                         Birthday
==  =====           ====          =====                         ========
.
# The Perl write statement will output a formatted line with values from the
# @row array. See http://perldoc.perl.org/perlform.html for details.
format STDOUT =
@>  @<<<<<<<<<<<<<  @<<<<<<<<<<<  @<<<<<<<<<<<<<<<<<<<<<<<<<<<  @<<<<<<<<<
@row
.
# Loop through result rows while we have them
while (@row = $sth->fetchrow_array()) {
         write; # Format command does the work of extracting the columns from
                # the @row array and writing them out to STDOUT.
}
# Call commit to prevent Perl from complaining about uncommitted transactions
# when disconnecting
$dbh->commit();
$dbh->disconnect();

data.txt 是一个文本文件,其中的每一行都包含一行数据。列由管线字符 (|) 分隔。这是命令接受的默认 COPY 分隔符,它简化了 COPY LOCAL 语句。

下面是文件内容的示例:

1|Georgia|Gomez|Rhiannon@magna.us|1937-10-03
2|Abdul|Alexander|Kathleen@ipsum.gov|1941-03-10
3|Nigel|Contreras|Tanner@et.com|1955-06-01
4|Gray|Holt|Thomas@Integer.us|1945-12-06
5|Candace|Bullock|Scott@vitae.gov|1932-05-27
6|Matthew|Dotson|Keith@Cras.com|1956-09-30
7|Haviva|Hopper|Morgan@porttitor.edu|1975-05-10
8|Stewart|Sweeney|Rhonda@lectus.us|2003-06-20
9|Allen|Rogers|Alexander@enim.gov|2006-06-17
10|Trevor|Dillon|Eagan@id.org|1988-11-27
11|Leroy|Ashley|Carter@turpis.edu|1958-07-25
12|Elmo|Malone|Carla@enim.edu|1978-08-29
13|Laurel|Ball|Zelenia@Integer.us|1989-09-20
14|Zeus|Phillips|Branden@blandit.gov|1996-08-08
15|Alexis|Mclean|Flavia@Suspendisse.org|2008-01-07

对大型示例文件运行以上示例代码后,将生成以下输出:

Connected!
Loading file /home/dbadmin/Perl/data.txt
Copied 1000000 rows into database.
ID  First           Last          EMail                         Birthday
==  =====           ====          =====                         ========
 1  Georgia         Gomez         Rhiannon@magna.us             1937-10-03
 2  Abdul           Alexander     Kathleen@ipsum.gov            1941-03-10
 3  Nigel           Contreras     Tanner@et.com                 1955-06-01
 4  Gray            Holt          Thomas@Integer.us             1945-12-06
 5  Candace         Bullock       Scott@vitae.gov               1932-05-27
 6  Matthew         Dotson        Keith@Cras.com                1956-09-30
 7  Haviva          Hopper        Morgan@porttitor.edu          1975-05-10
 8  Stewart         Sweeney       Rhonda@lectus.us              2003-06-20
 9  Allen           Rogers        Alexander@enim.gov            2006-06-17
10  Trevor          Dillon        Eagan@id.org                  1988-11-27
11  Leroy           Ashley        Carter@turpis.edu             1958-07-25
12  Elmo            Malone        Carla@enim.edu                1978-08-29
13  Laurel          Ball          Zelenia@Integer.us            1989-09-20
14  Zeus            Phillips      Branden@blandit.gov           1996-08-08

2.3.5.6 - 使用 Perl 执行查询

要使用 Perl 查询 Vertica:

  1. 使用 Perl DBI 模块的 prepare 函数准备查询语句。此函数将返回一个语句句柄,您可以使用此句柄来执行查询和获取结果集。

  2. 通过对该语句句柄调用 execute 函数来执行预定义的语句。

  3. 使用多种方法之一从语句句柄检索查询结果,例如,调用语句句柄的 fetchrow_array 函数以检索数据行,或者调用 fetchall_array 以获取包含整个结果集的一系列数组(如果结果集可能非常大,则此方法不太适用!)。

以下示例演示了查询由使用 Perl 批量加载数据中所示的示例创建的表。此示例将执行查询以检索该表的所有内容,然后重复调用 fetchrow_array 函数以获取数组中的数据行。此示例将重复上述过程直至 fetchrow_array 返回 undef 为止,该返回值表示没有更多数据行可供读取。

#!/usr/bin/perl
use strict;
use DBI;
my $attr = {RaiseError => 1 }; # Make errors fatal to the Perl script.
# Open a connection using a DSN. Supply the username and password.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123",
                        $attr);
# Prepare a query to get the content of the table
my $sth = $dbh->prepare("SELECT * FROM TEST ORDER BY C_ID ASC");
# Execute the query by calling execute on the statement handle
$sth->execute();
# Loop through result rows while we have them, getting each row as an array
while (my @row = $sth->fetchrow_array()) {
    # The @row array contains the column values for this row of data
    # Loop through the column values
    foreach my $column (@row) {
        if (!defined $column) {
            # NULLs are signaled by undefs. Set to NULL for clarity
            $column = "NULL";
        }
        print "$column\t"; # Output the column separated by a tab
    }
    print "\n";
}
$dbh->disconnect();

该示例在运行后输出了以下内容:

1    1.111    Hello World!    2001-01-01    01:01:01    2001-01-01 01:01:01    1
2    2.22222    How are you?    2002-02-02    02:02:02    2002-02-02 02:02:02    0
4    4.22222    NULL    2002-02-02    02:02:02    2002-02-02 02:02:02    0

将变量绑定到列值

检索查询结果的另一种方法是使用语句句柄的 bind_columns 函数将变量绑定到结果集中的列。如果需要对返回的数据执行大量处理,您可能会这种方法很方便,因为代码可以使用变量而非数组引用来访问数据。以下示例演示了将变量绑定到结果集而非在行值和列值中循环。

#!/usr/bin/perl
use strict;
use DBI;
my $attr = {RaiseError => 1 }; # Make SQL errors fatal to the Perl script.
# Open a connection using a DSN. Supply the username and password.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN32","ExampleUser","password123",
                        $attr);
# Prepare a query to get the content of the table
my $sth = $dbh->prepare("SELECT * FROM TEST ORDER BY C_ID ASC");
$sth->execute();
# Create a set of variables to bind to the column values.
my ($C_ID, $C_FP, $C_VARCHAR, $C_DATE, $C_TIME, $C_TS, $C_BOOL);
# Bind the variable references to the columns in the result set.
$sth->bind_columns(\$C_ID, \$C_FP, \$C_VARCHAR, \$C_DATE, \$C_TIME,
                    \$C_TS, \$C_BOOL);

# Now, calling fetch() to get a row of data updates the values of the bound
# variables. Continue calling fetch until it returns undefined.
while ($sth->fetch()) {
    # Note, you should always check that values are defined before using them,
    # since NULL values are translated into Perl as undefined. For this
    # example, just check the VARCHAR column for undefined values.
    if (!defined $C_VARCHAR) {
        $C_VARCHAR = "NULL";
    }
    # Just print values separated by tabs.
    print "$C_ID\t$C_FP\t$C_VARCHAR\t$C_DATE\t$C_TIME\t$C_TS\t$C_BOOL\n";
}
$dbh->disconnect();

此示例的输出与上一个示例的输出相同。

准备、查询和返回单个行

如果希望查询结果是单个行(例如在执行 COUNT (*) 查询时),您可以使用 DBI 模块的 selectrow_array 函数将语句执行和数组检索合并为单个结果。

以下示例显示了使用 selectrow_array 执行 SHOW LOCALE 语句并获取其结果。此示例还演示了使用 do 函数更改区域设置。

#!/usr/bin/perl
use strict;
use DBI;
my $attr = {RaiseError => 1 }; # Make SQL errors fatal to the Perl script.
# Open a connection using a DSN. Supply the username and password.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123",
                        $attr);
# Demonstrate setting/getting locale.
# Use selectrow_array to combine preparing a statement, executing it, and
# getting an array as a result.
my @localerv = $dbh->selectrow_array("SHOW LOCALE;");
# The locale name is the 2nd column (array index 1) in the result set.
print "Locale: $localerv[1]\n";
# Use do() to execute a SQL statement to set the locale.
$dbh->do("SET LOCALE TO en_GB");
# Get the locale again.
@localerv = $dbh->selectrow_array("SHOW LOCALE;");
print "Locale is now: $localerv[1]\n";
$dbh->disconnect();

运行此示例后的结果如下所示:

Locale: en_US@collation=binary (LEN_KBINARY)
Locale is now: en_GB (LEN)

执行查询和 ResultBufferSize 设置

当您在准备好的语句上调用 execute() 函数时,客户端库会一直检索结果,直到达到结果缓冲区大小。结果缓冲区大小是使用 ODBC 的 ResultBufferSize 设置进行设置的。

Vertica 不允许每个连接有多个活动查询。不过,您可以模拟多个活动查询,方法是将结果缓冲区设置得大到足以容纳第一个查询的全部结果。为了确保 ODBC 客户端驱动程序的缓冲区大到足以存储第一个查询的结果集,您可以将 ResultBufferSize 设置为 0。将此参数设置为 0 会使结果缓冲区大小不受限制。ODBC 驱动程序分配足够的内存来读取整个结果集。将第一个查询的整个结果集存储在结果集缓冲区后,数据库连接便可以自由地执行另一个查询。您的客户端可以执行第二个查询,即使它尚未处理第一个查询中的整个结果集。

但是,如果您将 ResultBufferSize 设置为 0,您可能会发现对 execute() 的调用会导致操作系统终止您的 Perl 客户端脚本。如果 ODBC 驱动程序分配了太多内存来存储大型结果集,操作系统可能会终止您的脚本。

此行为的解决方法是限制查询返回的行数。然后,您可以将 ResultBufferSize 设置为适合此有限结果集的值。例如,您可以估计为了存储单行查询结果而所需的内存量。然后,使用 LIMITOFFSET 子句来获得适合您借助于 ResultBufferSize 所分配空间的具体行数。如果查询结果能够适合有限结果集缓冲区,则您可以使用相同的数据库连接来执行其他查询。此解决方案使您的代码更加复杂,因为您需要执行多个查询才能获得整个结果集。此外,如果您需要一次对整个结果集进行操作,而不是一次只对其一部分进行操作,那么此解决方案也不适合。

更好的解决方案是针对要执行的每个查询使用单独的数据库连接。与处理大型数据集所需的资源相比,建立额外数据库连接所需的开销很小。

2.3.5.7 - Perl 数据类型和 Vertica 数据类型之间的转换

Perl 是一种弱类型编程语言,这种语言不为值分配特定的数据类型。这种语言基于对值执行的操作在字符串和数值之间转换。因此,Perl 在从 Vertica 提取大部分字符串和数值数据类型时遇到的问题较小。所有时间间隔数据类型(DATE 和 TIMESTAMP 等)会转换为字符串。可以使用多个不同的 Perl 日期和时间处理模块在脚本中处理这些值。

Vertica NULL 值会转换为 Perl 的未定义的值 (undef)。从包含 NULL 值的列读取数据时,始终应在使用某个值之前测试是否已定义该值。

将数据插入到 Vertica 时,Perl DBI 模块会尝试将数据强制转换为正确格式。默认情况下,该模块假设列值为 VARCHAR,除非它能够确定这些列值为某种其他数据类型。如果提供了一个字符串值以插入到具有整数或数值数据类型的列,DBI 会尝试将该字符串的内容转换为正确的数据类型。如果整个字符串可以转换为适当数据类型的值,该模块会将值插入到列中。否则,插入数据行将失败。

当插入到 FLOAT、NUMERIC 或类似数据类型的列时,DBI 以透明方式将整数值转换为数值或浮点值。仅当不会降低精度(小数点右边的值为 0)时,该模块才会将数值或浮点值转换为整数。例如,该模块可以将值 3.0 插入到 INTEGER 列中,因为将该值转换为整数时不会降低精度。该模块无法将 3.1 插入到 INTEGER 列中,因为这样会降低精度。该模块将返回错误,而非将该值截断为 3。

以下示例演示了 DBI 模块在将数据插入到 Vertica 时执行的一些转换。

#!/usr/bin/perl
use strict;
use DBI;
# Create a hash reference that holds a hash of parameters for the
# connection.
 my $attr = {AutoCommit => 0, # Turn off autocommit
             PrintError => 0   # Turn off print error. Manually handled
             };
# Open a connection using a DSN. Supply the username and password.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123",
    $attr);
if (defined DBI::err) {
    # Conection failed.
    die "Failed to connect: $DBI::errstr";
}
print "Connection AutoCommit state is: " . $dbh->{AutoCommit} . "\n";
# Create table to hold inserted data
$dbh->do("DROP TABLE IF EXISTS TEST CASCADE;");
$dbh->do("CREATE TABLE TEST( \
               C_ID  INT, \
               C_FP  FLOAT,\
               C_VARCHAR VARCHAR(100),\
               C_DATE DATE, C_TIME TIME,\
               C_TS TIMESTAMP,\
               C_BOOL BOOL)");
# Populate an array of arrays with values.
my @data = (
                # Start with matching data types
                [1,1.111,'Matching datatypes','2001-01-01','01:01:01'
                    ,'2001-01-01 01:01:01','t'],
                # Force floats -> int and int -> float.
                [2.0,2,"Ints <-> floats",'2002-02-02','02:02:02'
                    ,'2002-02-02 02:02:02',1],
                # Float -> int *only* works when there is no loss of precision.
                # this row will fail to insert:
                [3.1,3,"float -> int with trunc?",'2003-03-03','03:03:03'
                    ,'2003-03-03 03:03:03',1],
                # String values are converted into numbers
                ["4","4.4","Strings -> numbers", '2004-04-04','04:04:04',
                    ,'2004-04-04 04:04:04',0],
                # String -> numbers only works if the entire string can be
                # converted into a number
                ["5 and a half","5.5","Strings -> numbers", '2005-05-05',
                    '05:05:05', ,'2005-05-05 05:05:05',0],
                # Number are converted into string values automatically,
                # assuming they fit into the column width.
                [6,6.6,3.14159, '2006-06-06','06:06:06',
                    ,'2006-06-06 06:06:06',0],
                # There are some variations in the accepted date strings
                [7,7.7,'Date/time formats', '07/07/2007','07:07:07',
                    ,'07-07-2007 07:07:07',1],
             );
# Create a prepared statement to use parameters for inserting values.
my $sth = $dbh->prepare_cached("INSERT into test values(?,?,?,?,?,?,?)");
my $rowcount = 0; # Count # of rows
# Loop through the arrays to insert values
foreach my $tuple (@data) {
    $rowcount++;
    # Insert the row
    my $retval = $sth->execute(@$tuple);

    # See if the row was successfully inserted.
    if ($retval == 1) {
        # Value of 1 means the row was inserted (1 row was affected by insert)
        print "Row $rowcount successfully inserted\n";
    } else {
        print "Inserting row $rowcount failed with error " .
                $sth->state . " " . $sth->errstr . "\n";
    }
}
# Commit the data
$dbh->commit();
# Prepare a query to get the content of the table
$sth = $dbh->prepare("SELECT * FROM TEST ORDER BY C_ID ASC");
$sth->execute() or die "Error: " . $dbh->errstr;
my @row; # Need to pre-declare to use in the format statement.
# Use Perl formats to pretty print the output.
format STDOUT_TOP =
Int  Float          VarChar        Date      Time      Timestamp     Bool
===  =====  ==================  ========== ======== ================ ====
.
format STDOUT =
@>>  @<<<<  @<<<<<<<<<<<<<<<<<  @<<<<<<<<< @<<<<<<< @<<<<<<<<<<<<<<< @<<<<
@row
.
# Loop through result rows while we have them
while (@row = $sth->fetchrow_array()) {
         write; # Format command does the work of extracting the columns from
                 # the array.
}
# Commit to stop Perl complaining about in-progress transactions.
$dbh->commit();
$dbh->disconnect();

运行该示例后,将生成以下输出:

Connection AutoCommit state is: 0
Row 1 successfully inserted
Row 2 successfully inserted
Inserting row 3 failed with error 01000 [Vertica][VerticaDSII] (20) An error
occurred during query execution: Row rejected by server; see server log for
details (SQL-01000)
Row 4 successfully inserted
Inserting row 5 failed with error 01000 [Vertica][VerticaDSII] (20) An error
occurred during query execution: Row rejected by server; see server log for
details (SQL-01000)
Row 6 successfully inserted
Row 7 successfully inserted
Int  Float          VarChar        Date      Time      Timestamp     Bool
===  =====  ==================  ========== ======== ================ ====
  1  1.111  Matching datatypes  2001-01-01 01:01:01 2001-01-01 01:01 1
  2  2      Ints <-> floats     2002-02-02 02:02:02 2002-02-02 02:02 1
  4  4.4    Strings -> numbers  2004-04-04 04:04:04 2004-04-04 04:04 0
  6  6.6    3.14159             2006-06-06 06:06:06 2006-06-06 06:06 0
  7  7.7    Date/time formats   2007-07-07 07:07:07 2007-07-07 07:07 1

2.3.5.8 - Perl Unicode 支持

Perl 支持 Unicode 数据,但具有一些限制。有关详细信息,请参阅 perlunicode 手册页和 perlunitut(Perl Unicode 教程)手册页。(务必参阅随客户端系统上安装的 Perl 版本附带提供的这些手册页副本,因为 Unicode 支持在最新版本的 Perl 中已更改。)Perl DBI 和 DBD::ODBC 也支持 Unicode,但必须为 DBD::ODBC 编译 Unicode 支持。有关详细信息,请参阅 DBD::ODBC 文档。可以检查名为 odbc_has_unicode 的特定于 DBD::ODBC 的连接属性,以查看是否在该驱动程序中启用了 Unicode 支持。

以下示例 Perl 脚本演示了直接将 UTF-8 字符串插入到 Vertica 再从中读回这些字符串。此示例将输出写入到文本文件中,因为在终端窗口或控制台中显示 Unicode 字符时出现许多问题。

#!/usr/bin/perl
use strict;
use DBI;
# Open a connection using a DSN.
my $dbh = DBI->connect("dbi:ODBC:VerticaDSN","ExampleUser","password123");
unless (defined $dbh) {
    # Conection failed.
    die "Failed to connect: $DBI::errstr";
}
# Output to a file. Displaying Unicode characters to a console or terminal
# window has many problems. This outputs a UTF-8 text file that can
# be handled by many Unicode-aware text editors:
open OUTFILE, '>:utf8', "unicodeout.txt";
# See if the DBD::ODBC driver was compiled with Unicode support. If this returns
# 1, your Perl script will get get strings from the driver with the UTF-8
# flag set on them, ensuring that Perl handles them correctly.
print OUTFILE "Was DBD::ODBC compiled with Unicode support? " .
    $dbh->{odbc_has_unicode} . "\n";

# Create a table to hold VARCHARs
$dbh->do("DROP TABLE IF EXISTS TEST CASCADE;");

# Create a table to hold data. Remember that the width of the VARCHAR column
# is the number of bytes set aside to store strings, which often does not equal
# the number of characters it can hold when it comes to Unicode!
$dbh->do("CREATE TABLE test( C_VARCHAR VARCHAR(100) )");
print OUTFILE "Inserting data...\n";
# Use Do to perform simple inserts
$dbh->do("INSERT INTO test VALUES('Hello')");
# This string contains several non-latin accented characters and symbols, encoded
# with Unicode escape notation. They are converted by Perl into UTF-8 characters
$dbh->do("INSERT INTO test VALUES('My favorite band is " .
    "\N{U+00DC}ml\N{U+00E4}\N{U+00FC}t \N{U+00D6}v\N{U+00EB}rk\N{U+00EF}ll" .
    " \N{U+263A}')");
# Some Chinese (Simplified) characters. This again uses escape sequence
# that Perl translates into UTF-8 characters.
$dbh->do("INSERT INTO test VALUES('\x{4F60}\x{597D}')");
print OUTFILE "Getting data...\n";
# Prepare a query to get the content of the table
my $sth = $dbh->prepare_cached("SELECT * FROM test");
# Execute the query by calling execute on the statement handle
$sth->execute();
# Loop through result rows while we have them
while (my @row = $sth->fetchrow_array()) {
    # Loop through the column values
    foreach my $column (@row) {
        print OUTFILE "$column\t";
    }
    print OUTFILE "\n";
}
close OUTFILE;
$dbh->disconnect();

在支持 UTF-8 的文本编辑器或查看器中查看 unicodeout.txt 文件时,显示以下内容:

Was DBD::ODBC compiled with Unicode support? 1
Inserting data...
Getting data...
My favorite band is Ümläüt Övërkïll ☺
你好
Hello

另请参阅

2.3.6 - Python

Vertica Python 驱动程序为 Python 客户端应用程序提供与数据库交互的接口。

先决条件

在创建 Python 客户端应用程序之前,必须配置 Python 开发环境

2.3.6.1 - 在 Linux 中配置 ODBC 运行时环境,请执行下列操作:

若要在 Linux 中配置 ODBC 运行时环境,请执行下列操作:

  1. 创建 odbc.ini 文件(如果此文件尚未存在)。

  2. 将 ODBC 驱动程序目录添加到 LD_LIBRARY_PATH 系统环境变量:

    export LD_LIBRARY_PATH=/path-to-vertica-odbc-driver:$LD_LIBRARY_PATH
    

这些步骤仅与 unixODBC 和 iODBC 相关。有关 odbc.ini 的详细信息,请参阅其相应文档。

另请参阅

2.3.6.2 - 使用 pyodbc 查询数据库

以下示例会话将 pyodbc 与 Vertica ODBC 驱动程序结合使用,以将 Python 连接到 Vertica 数据库。

示例脚本

以下示例脚本显示了如何使用 Python 3、pyodbc 和 ODBC DSN 查询 Vertica。


import pyodbc
cnxn = pyodbc.connect("DSN=VerticaDSN", ansi=True)
cursor = cnxn.cursor()
# create table
cursor.execute("CREATE TABLE TEST("
    "C_ID  INT,"
    "C_FP  FLOAT,"
    "C_VARCHAR VARCHAR(100),"
    "C_DATE DATE, C_TIME TIME,"
    "C_TS TIMESTAMP,"
    "C_BOOL BOOL)")
cursor.execute("INSERT INTO test VALUES(1,1.1,'abcdefg1234567890','1901-01-01','23:12:34','1901-01-01 09:00:09','t')")
cursor.execute("INSERT INTO test VALUES(2,3.4,'zxcasdqwe09876543','1991-11-11','00:00:01','1981-12-31 19:19:19','f')")
cursor.execute("SELECT * FROM TEST")
rows = cursor.fetchall()
for row in rows:
    print(row, end='\n')
cursor.execute("DROP TABLE TEST CASCADE")
cursor.close()
cnxn.close()

生成的输出如下所示:

(2, 3.4, 'zxcasdqwe09876543', datetime.date(1991, 11, 11), datetime.time(0, 0, 1), datetime.datetime(1981, 12, 31, 19, 19, 19), False)
(1, 1.1, 'abcdefg1234567890', datetime.date(1901, 1, 1), datetime.time(23, 12, 34), datetime.datetime(1901, 1, 1, 9, 0, 9), True)

注意

对于未命名的主键约束,SQLPrimaryKeys 将返回主键 (pk_name) 列中的表名。例如:

  • 未命名主键:

    CREATE TABLE schema.test(c INT PRIMARY KEY);
    
    SQLPrimaryKeys
    "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", "KEY_SEQ", "PK_NAME" <Null>, "SCHEMA", "TEST", "C", 1, "TEST"
    
  • 已命名主键:

    CREATE TABLE schema.test(c INT CONSTRAINT pk_1 PRIMARY KEY);
    
    SQLPrimaryKeys
    "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", "KEY_SEQ", "PK_NAME" <Null>, "SCHEMA", "TEST", "C", 1, "PK_1"
    

Micro Focus 建议对约束进行命名。

另请参阅

2.3.7 - PHP

设置

在通过 PHP 连接到 Vertica 之前,必须阅读 C/C++。以下示例 ODBC 配置条目详细介绍了 PHP ODBC 连接所需的典型设置。驱动程序位置假设您已将 Vertica 驱动程序复制到 /usr/lib64

示例 odbc.ini

[ODBC Data Sources]
VerticaDSNunixodbc = exampledb
VerticaDNSiodbc = exampledb2
[VerticaDSNunixodbc]
Description = VerticaDSN Unix ODBC driver
Driver = /usr/lib64/libverticaodbc.so
Database = Telecom
Servername = localhost
UserName = dbadmin
Password =
Port = 5433
[VerticaDSNiodbc]
Description = VerticaDSN iODBC driver
Driver = /usr/lib64/libverticaodbc.so
Database = Telecom
Servername = localhost
UserName = dbadmin
Password =
Port = 5433

示例 odbcinst.ini

# Vertica
[VerticaDSNunixodbc]
Description = VerticaDSN Unix ODBC driver
Driver = /usr/lib64/libverticaodbc.so
[VerticaDNSiodbc]
Description = VerticaDSN iODBC driver
Driver = /usr/lib64/libverticaodbc.so
[ODBC]
Threading = 1

验证 Vertica UnixODBC 或 iODBC 库

可以使用以下命令验证 Vertica UnixODBC 库是否能够加载所有从属库(假设您已将这些库复制到 /usr/lib64):

例如:

ldd /usr/lib64/libverticaodbc.so

您必须先解决找不到任何库的问题才能继续操作。

测试 ODBC 连接

使用以下命令测试 ODBC 连接。

isql -v VerticaDSN

2.3.7.1 - 配置 PHP 开发环境

要配置 PHP 开发环境:

  1. 安装和配置 ODBC

  2. 安装 PHP。

  3. 安装 PDO 和 ODBC PHP 扩展。在 Linux 上,它们可以作为以下软件包使用:

    • php-odbc

    • php-pdo

2.3.7.2 - PHP Unicode 支持

PHP 不支持本机 Unicode 支持。PHP 仅支持 256 位字符集。但是,PHP 提供了 UTF-8 函数 utf8_encode()utf8_decode(),从而提供某种基本 Unicode 功能。

有关 PHP 和 Unicode 的更多详细信息,请参阅 PHP 手册的 strings 字符串一节。

2.3.7.3 - 使用 PHP 查询数据库

以下示例脚本详细介绍了使用 PHP ODBC 函数连接到 Vertica Analytics Platform。

<?php
# Turn on error reporting
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
# A simple function to trap errors from queries
function odbc_exec_echo($conn, $sql) {
    if(!$rs = odbc_exec($conn,$sql)) {
        echo "<br/>Failed to execute SQL: $sql<br/>" . odbc_errormsg($conn);
    } else {
        echo "<br/>Success: " . $sql;
    }
    return $rs;
}
# Connect to the Database
$dsn = "VerticaDSNunixodbc";
$conn = odbc_connect($dsn,'','') or die ("<br/>CONNECTION ERROR");
echo "<p>Connected with DSN: $dsn</p>";
# Create a table
$sql = "CREATE TABLE TEST(
        C_ID INT,
        C_FP FLOAT,
        C_VARCHAR VARCHAR(100),
        C_DATE DATE, C_TIME TIME,
        C_TS TIMESTAMP,
        C_BOOL BOOL)";
$result = odbc_exec_echo($conn, $sql);
# Insert data into the table with a standard SQL statement
$sql = "INSERT into test values(1,1.1,'abcdefg1234567890','1901-01-01','23:12:34
','1901-01-01 09:00:09','t')";
$result = odbc_exec_echo($conn, $sql);
# Insert data into the table with odbc_prepare and odbc_execute
$values = array(2,2.28,'abcdefg1234567890','1901-01-01','23:12:34','1901-01-01 0
9:00:09','t');
$statement = odbc_prepare($conn,"INSERT into test values(?,?,?,?,?,?,?)");
if(!$result = odbc_execute($statement, $values)) {
            echo "<br/>odbc_execute Failed!";
} else {
            echo "<br/>Success: odbc_execute.";
}
# Get the data from the table and display it
$sql = "SELECT * FROM TEST";
if($result = odbc_exec_echo($conn, $sql)) {
    echo "<pre>";
    while($row = odbc_fetch_array($result) ) {
            print_r($row);
    }
    echo "</pre>";
}
# Drop the table and projection
$sql = "DROP TABLE TEST CASCADE";
$result = odbc_exec_echo($conn, $sql);
# Close the ODBC connection
odbc_close($conn);
?>

示例输出

以下是脚本生成的示例输出。

Success: CREATE TABLE TEST( C_ID INT, C_FP FLOAT, C_VARCHAR VARCHAR(100), C_DATE DATE, C_TIME TIME, C_TS TIMESTAMP, C_BOOL BOOL)
Success: INSERT into test values(1,1.1,'abcdefg1234567890','1901-01-01','23:12:34 ','1901-01-01 09:00:09','t')
Success: odbc_execute.
Success: SELECT * FROM TEST
Array
(
    [C_ID] => 1
    [C_FP] => 1.1
    [C_VARCHAR] => abcdefg1234567890
    [C_DATE] => 1901-01-01
    [C_TIME] => 23:12:34
    [C_TS] => 1901-01-01 09:00:09
    [C_BOOL] => 1
)
Array
(
    [C_ID] => 2
    [C_FP] => 2.28
    [C_VARCHAR] => abcdefg1234567890
    [C_DATE] => 1901-01-01
    [C_TIME] => 23:12:34
    [C_TS] => 1901-01-01 23:12:34
    [C_BOOL] => 1
)
Success: DROP TABLE TEST CASCADE

2.4 - 管理客户端和 Vertica 之间的查询执行

以下主题介绍了可帮助您管理客户端和 Vertica 数据库之间的查询执行的技术。

2.4.1 - ResultBufferSize

默认情况下,Vertica 使用 ResultBufferSize 参数来确定客户端可从服务器检索的结果集的最大大小(以字节为单位)。如果启用了 ResultBufferSize,则 Vertica 会直接将数据行发送到发出查询的客户端。每次数据提取返回到客户端的行数取决于 ResultBufferSize 参数的大小(以字节为单位)。

有时,客户端请求的结果集大小会大于 ResultBufferSize 参数所允许的大小。在这种情况下,Vertica 一次只会检索结果集的一部分。每次数据提取将返回等于 ResultBufferSize 参数所设置的大小的数据量。最后,随着客户端循环访问各次数据提取,将返回整个结果集。

ResultBufferSize 的优点

如果您担心查询对网络延迟造成的影响,ResultBufferSize 可提供比 MARS 更有利的优势。MARS 要求客户端等待直至所有数据行都已写入到服务器,然后才能检索数据。由于需要等待存储结果,此延迟可能会造成网络延迟问题。

此外,MARS 要求发送两个单独的请求以返回数据行。第一个请求完成查询执行,此步骤会将结果集存储在服务器上。第二个请求检索存储在服务器上的数据行。使用 ResultBufferSize 时,您只需要发送一个请求。此请求会执行并检索相关数据行。

使用 ResultBufferSize 的查询执行

下图显示了已启用 ResultBufferSize 时 Vertica 如何将数据库中的数据行返回到客户端:

查询执行将完成以下步骤:

  1. 客户端将查询(例如 SELECT 语句)发送到服务器。在上图中,第一个查询名为查询 1。

  2. 服务器接收客户端的请求,并开始将结果集的描述和请求的数据行发送回客户端。

  3. 所有可能的行均已返回到客户端之后,执行便已完成。返回的数据集的大小等于已请求的数据的大小,或者等于 ResultBufferSize 参数可检索的最大数据量。如果尚未达到 ResultBufferSize 最大大小,则 Vertica 可以执行查询 2。

服务器可以接受查询 2,并执行与查询 1 相同的步骤。如果查询 1 的结果已达到 ResultBufferSize 可允许的最大大小,则在客户端释放查询 1 的结果之前,Vertica 无法执行查询 2。

查询 2 运行之后,您无法查看为查询 1 检索的结果,除非再次执行查询 1。

设置无限的缓冲区大小

将 ResultBufferSize 设置为 0 会告知客户端驱动程序使用无限的结果集缓冲区。使用此设置,客户端库将分配尽可能多的内存来读取查询的整个结果集。您可以选择将 ResultBufferSize 设置为 0,以模拟通过单个数据库连接同时进行多个活动查询。由于缓冲区大小不受限制,您的客户端可以运行查询并将其整个结果集存储在内存中。这将结束第一个查询,因此您的客户端可以在完全处理第一个查询的结果之前执行第二个查询。

此方法的一个缺点是,如果您的查询返回较大的结果集,您的查询可能会消耗过多的内存。这种内存过度分配可能会导致操作系统终止您的客户端。由于存在这种风险,请考虑使用多个数据库连接,而不是尝试针对多个查询重用单个连接。与处理大型数据集所需的资源总量相比,多个数据库连接的开销很小。

2.4.2 - 多重活动结果集 (MARS)

只有在使用 JDBC 客户端连接与 Vertica 连接时才能启用 MARS。MARS 允许在单个连接中执行多个查询。ResultBufferSize 将查询的结果直接发送到客户端,而 MARS 先将结果存储在服务器上。完成查询和存储所有结果之后,您可向服务器发送检索请求以将行返回到客户端。

MARS 在会话级别进行设置,并且必须为每个新的会话启用。如果启用了 MARS,则会禁用 ResultBufferSize。不会返回任何错误,但会忽略 ResultBufferSize 参数。

MARS 的优点

与 ResultBufferSize 相比,MARS 允许同时存储来自不同查询的多个结果集。您还可以在前一个结果集的所有结果都已返回到客户端之前发送新的查询。这样可使应用程序能够将查询执行与结果检索分析,以便您可以在单个连接上同时处理不同的结果。

当启用 ResultBufferSize 时,您必须等待直至所有结果集都已返回到客户端,然后才能发送新的查询。

MARS 的另一个优点是,它释放资源的速度比 ResultBufferSize 更快。当查询正在运行时,资源由该查询会话占有。如果启用了 ResultBufferSize,性能低下的客户端可能会读取结果集的单个行,然后必须停下来以检索下一行。这样会导致查询无法快速完成,从而导致无法释放已用资源以用于其他应用程序。使用 MARS 时,客户端的速度与行的读取无关。结果写入到 MARS 存储后,将立即释放资源,而且客户端检索行的速度不再会成为问题。

使用 MARS 的查询执行

下图演示了已启用 MARS 时如何处理发送到服务器的多个查询:

查询 1:

  1. 查询 1 已发送到服务器。

  2. 查询 1 的行描述及其结果集状态已返回给客户端。但是,此时没有将结果返回给客户端。

  3. 查询 1 将完成,并且其结果会保存在服务器上。

    1. 现在,您可以发送命令以检索查询 1 的结果集的行。这些行存储在服务器上。已检索的行会和结果集的状态一起发送到客户端。通过跟踪结果集的状态,Vertica 能够跟踪已从服务器检索了哪些行。
  4. 查询 1 已成功完成,并且其结果集正存储在服务器上,现在可以执行查询 2 了。

查询 2:

  1. 查询 2 已发送到服务器。

  2. 查询 2 的行描述及其结果集状态已返回给客户端。但是,此时没有将结果返回给客户端。

  3. 查询 2 将完成,并且其结果会存储在服务器上。现在,查询 1 和查询 2 的结果集都已存储在服务器上。

  4. 现在,您可以将检索请求发送到查询 1 和查询 2 已存储在服务器上的结果集。每次对查询 1 中的行发出请求时,将发送请求,并且行和结果集状态会发送到客户端。查询 2 也会出现相同。

客户端已读取所有行后,服务器上的 MARS 存储会关闭活动的结果会话。然后会释放服务器上的 MARS 存储,以便存储更多数据。会话完成后,MARS 存储也会关闭并释放。

启用和禁用 MARS

可以通过以下两种不同方法启用和禁用 MARS:

  1. 要使用 JDBC 客户端连接属性启用 MARS,请参阅 JDBC 连接属性

  2. 要使用 SET SESSION 命令启用 MARS,请参阅 SET SESSION MULTIPLEACTIVERESULTSETS

另请参阅

3 - 管理 API

管理 API 是一个 REST API,您可以使用此 API 通过接受 REST 和 JSON 的脚本或应用程序查看和管理 Vertica 数据库。所有请求的响应格式均为 JSON。

3.1 - cURL

cURL 是一个命令行工具兼应用程序库,用于将数据传输到服务器以及从中传输数据。所有发送到 Vertica 服务器的 API 请求都必须使用 HTTPS 发出。

可以使用 cURL 传递以下四种 HTTP 请求以调用 API 方法:

  • GET:检索数据。

  • PUT:更新数据。

  • POST:创建新数据。

  • DELETE:删除数据。

语法

curl https://<NODE>:5444/

选项

下面是截断的选项列表。如需完整的列表,请参阅 cURL 文档

配置 HTTPS

HTTPS 使用 TLS 对您的连接加密。以下过程使用预定义的 server TLS 配置启用 HTTPS。要创建自定义 TLS 配置,请参阅TLS 配置

  1. 根据您的用例生成或导入以下内容:

    • 服务器模式:服务器证书私钥、服务器证书

    • 相互模式:服务器证书私钥、服务器证书、CA 证书

  2. 根据您所需的配置运行以下命令。新连接将使用 TLS。

    • 要使用服务器模式,请为 server TLS 配置设置服务器证书:

      => ALTER TLS CONFIGURATION server CERTIFICATE server_cert;
      
    • 要使用相互模式,请设置服务器和 CA 证书。此 CA 证书用于验证客户端证书:

      => ALTER TLS CONFIGURATION server CERTIFICATE server_cert ADD CA CERTIFICATES ca_cert;
      

      要使用多个 CA 证书,请用逗号分隔它们:

      => ALTER TLS CONFIGURATION server CERTIFICATE server_cert
         ADD CA CERTIFICATES intermediate_ca_cert, ca_cert;
      
  3. 启用 TLS(默认情况下禁用)。选择以下按安全性升序列出的 TLSMODE 之一。

    • DISABLE:禁用 TLS。此参数的所有其他选项都启用 TLS。

    • ENABLE:启用 TLS。Vertica 不检查客户端证书。

    • TRY_VERIFY:如果出现以下任一情况,则建立 TLS 连接:

      • 客户出示有效证书

      • 客户没有出示证书

      如果客户端提供无效证书,则连接将使用纯文本。

    • VERIFY_CA:如果 Vertica 验证客户端证书来自受信任的 CA,则连接成功。如果客户端不提供客户端证书,则连接使用纯文本。

    TLS 配置也支持 TLSMODE VERIFY_FULL,但 HTTPS 不支持此 TLSMODE,其行为类似于 VERIFY_CA

    对于服务器模式,选择 ENABLE

    => ALTER TLS CONFIGURATION server TLSMODE 'ENABLE';
    

    对于相互模式,选择 TRY_VERIFY 或更高版本:

    => ALTER TLS CONFIGURATION server TLSMODE 'VERIFY_CA';
    
  4. 验证 HttpsTLSConfig 参数是否设置为 server TLS 配置:

    => SHOW CURRENT HttpsTLSConfig;
      level  |      name       | setting
    ---------+-----------------+---------
     DEFAULT | HttpsTLSConfig  | server
    (1 row)
    

    如果没有,请设置 HttpsTLSConfig 参数:

    => ALTER DATABASE DEFAULT SET HttpsTLSConfig = 'server';
    

3.2 - 常规 API 信息

这些 API 可以与标准 Vertica 节点或管理控制台节点交互。

3.2.1 - GET /

返回 API 版本信息和指向管理 API 的子资源的链接的列表。

资源 URL

https://<NODE>:5444/

身份验证

不是必需。

参数

无。

示例请求

响应:

{
    "body": {
        "mime-types": [
            "default",
            "application/vertica.database.configuration.json-v2",
            "application/json",
            "application/vertica.nodes.json-v2",
            "default",
            "application/json",
            "default",
            "application/json",
            "application/vertica.jobs.json-v2",
            "default",
            "application/vertica.hosts.json-v2",
            "application/json",
            "default",
            "application/vertica.hosts.json-v2",
            "application/json",
            "default",
            "application/json",
            "application/vertica.host.json-v2",
            "default",
            "application/vertica.hosts.json-v2",
            "application/json",
            "application/vertica.nodes.json-v2",
            "default",
            "application/json",
            "default",
            "application/json",
            "application/vertica.database.json-v2",
            "default",
            "application/vertica.hosts.json-v2",
            "application/json",
            "default",
            "application/vertica.hosts.json-v2",
            "application/json",
            "default",
            "application/json",
            "application/vertica.databases.json-v2",
            "application/vertica.nodes.json-v2",
            "default",
            "application/json",
            "application/vertica.agent.json-v2",
            "default",
            "application/json",
            "default",
            "application/vertica.users.json-v2",
            "application/json"
        ],
        "version": "7.1.0"
    },
    "href": "/",
    "links": [
        "/databases",
        "/hosts",
        "/nodes",
        "/licenses",
        "/webhooks",
        "/backups",
        "/restore",
        "/jobs"
    ],
    "mime-type": "application/vertica.agent.json-v2"
}

3.2.2 - GET api

列出所有管理 API 命令,并简要说明每个命令及其参数。

资源 URL

https://node-ip-address:5444/api

身份验证

示例

$ curl -k https://10.20.100.247:5444/api
[
   {
      "route": "/",
      "method": "GET",
      "description": "Returns the agent specific information useful for version checking and service discovery",
      "accepts": {},
      "params": []
   },
   {
      "route": "/api",
      "method": "GET",
      "description": "build the list of cluster objects and properties and return it as a JSON formatted array",
      "accepts": {},
      "params": []
   },
   {
      "route": "/backups",
      "method": "GET",
      "description": "list all the backups that have been created for all vbr configuration files ( *.ini ) that are located in the /opt/vertica/config directory.",
      "accepts": {},
      "params": []
   },
   {
      "route": "/backups/:config_script_base",
      "method": "POST",
      "description": "create a new backup as defined by the given vbr configuration script base (filename minus the .ini extenstion)",
      "accepts": {},
      "params": []
   },
   {
      "route": "/backups/:config_script_base/:archive_id",
      "method": "GET",
      "description": "get the  detail for a specific backup archive",
      "accepts": {},
      "params": []
   },
   {
      "route": "/backups/:config_script_base/:archive_id",
      "method": "DELETE",
      "description": "delete a backup based on the config ini file script",
      "accepts": {},
      "params": []
   },
   {
      "route": "/databases",
      "method": "GET",
      "description": "build the list of databases, their properties, and current status (from cache) and return it as a JSON formatted array",
      "accepts": {},
      "params": []
   },
   {
      "route": "/databases",
      "method": "POST",
      "description": "Create a new database by supplying a valid set of parameters",
      "accepts": {},
      "params": [
         "name    : name of the database to create",
         "passwd  : password used by the database administrative user",
         "only    : optional list of hostnames to include in database",
         "exclude : optional list of hostnames to exclude from the database",
         "catalog : directory used for the vertica catalog",
         "data    : directory used for the initial vertica storage location",
         "port    : port the database will listen on (default 5433)",
         "restart_policy : (optional) set restart policy",
         "force_cleanup_on_failure : (optional) Force removal of existing directories on failure of command",
         "force_removal_at_creation : (optional)  Force removal of existing directories before creating the database",
         "communal_storage_url : (optional) communal storage location for the database",
         "num_shards : (optional) number of shared for databases with communal storage",
         "depot_path : (optional, but if specified requires depot_size) path to a directory where files from communal storage can be locally cached",
         "depot_size : (optional, required by depot_path) size of the depot. Examples: (\"10G\", \"2000M\", \"1T\", \"250K\")",
         "aws_access_key_id: (optional)",
         "aws_secret_access_key : (optional)",
         "configuration_parameters : (optional) A string that is a serialized python-literal dictionary of configuration parameters set at bootstrap.
         '{\"kerberosservicename\":\"verticakerb\"}'"]
   },
   {
      "route": "/databases/:database_name",
      "method": "GET",
      "description": "Retrieve the database properties structure",
      "accepts": {},
      "params": []
   },
   {
      "route": "/databases/:database_name",
      "method": "PUT",
      "description": "Control / alter a database values using the PUT http method",
      "accepts": {},
      "params": ["action : value one of start|stop|rebalance|wla"]
   },
   {
      "route": "/databases/:database_name",
      "method": "DELETE",
      "description": "Delete an existing database",
      "accepts": {},
      "params": []
   },
   {
      "route": "/databases/:database_name/configuration",
      "method": "GET",
      "description": "retrieve the current parameters from the database. if its running return 503 Service Unavailable",
      "accepts": {},
      "params": [
         "user_id  : vertica database username",
         "passwd   : vertica database password"]
   },
   {
      "route": "/databases/:database_name/configuration",
      "method": "PUT",
      "description": "set a list of  parameters in the database. if its not running return 503 Service Unavailable",
      "accepts": {},
      "params": [
         "user_id   : vertica database username",
         "passwd    : vertica database password",
         "parameter : value  vertica parameter/key combo"]
   },
   ...
   {
      "route": "/webhooks/subscribe",
      "method": "POST",
      "description": "post a request with a callback url to subscribe to events from this agent.  Returns a subscription_id that can be used to unsubscribe from the service.  @returns  subscription_id",
      "accepts": {},
      "params": ["url : full url to the callback resource"]
   }
]

3.3 - 适用于代理的 Rest API

这些 API 调用与标准 Vertica 节点交互。

备份和还原

数据库

主机

作业

许可证

节点

Webhooks

3.3.1 - VerticaAPIKey

管理 API 需要使用名为 VerticaAPIKEY 的身份验证密钥来访问某些 API 资源。可以使用 apikeymgr 命令行工具管理 API 密钥。

usage: apikeymgr [-h] [--user REQUESTOR] [--app APPLICATION] [--delete]
                 [--create] [--update] [--migrate]
                 [--secure {restricted,normal,admin}] [--list]

API key management tool

optional arguments:
  -h, --help            show this help message and exit
  --user REQUESTOR      The name of the person requesting the key
  --app APPLICATION     The name of the application that will use the key
  --delete              Delete the key for the given R & A
  --create              Create a key for the given R & A
  --update              Update a key for the given R & A
  --migrate             migrate the keyset to the latest format
  --secure {restricted,normal,admin}
                        Set the keys security level
  --list                List all the keys known

示例请求

若要为 dbadmin 用户创建具有 admin 访问权限的新的 VerticaAPIKEY,请输入以下命令:

$ apikeymgr --user dbadmin --app vertica --create --secure admin

响应:

Requestor  : dbadmin
Application: vertica
API Key    : ValidAPIKey
Synchronizing cluster...

3.3.2 - 备份和还原

可以使用这些 API 调用执行数据库的备份和恢复任务。

3.3.2.1 - GET backups

返回已为 VBR 配置文件 (*.ini)(驻留在 /opt/vertica/config 中)创建的所有备份列表,并提供有关每个备份的详细信息。

资源 URL

https://<NODE>:5444/backups

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "data": [
        {
            "backups": [
                {
                    "archive_id": "v_vdb_bk_snapshot_20190305_174428",
                    "version": "v9.2.1-20190305",
                    "href": "/backups/fullbk/v_vdb_bk_snapshot_20190305_174428",
                    "exclude_patterns": "",
                    "backup_type": "full",
                    "include_patterns": "",
                    "epoch": "16",
                    "objects": "",
                    "hosts": "v_vdb_bk_node0001(10.20.91.240), v_vdb_bk_node0002(10.20.91.241), v_vdb_bk_node0003(10.20.91.242), v_vdb_bk_node0004(10.20.91.243), v_vdb_bk_node0005(10.20.91.244)"
                },
                {
                    "archive_id": "v_vdb_bk_snapshot_20190305_174025",
                    "version": "v9.2.1-20190305",
                    "href": "/backups/fullbk/v_vdb_bk_snapshot_20190305_174025",
                    "exclude_patterns": "",
                    "backup_type": "full",
                    "include_patterns": "",
                    "epoch": "16",
                    "objects": "",
                    "hosts": "v_vdb_bk_node0001(10.20.91.240), v_vdb_bk_node0002(10.20.91.241), v_vdb_bk_node0003(10.20.91.242), v_vdb_bk_node0004(10.20.91.243), v_vdb_bk_node0005(10.20.91.244)"
                }
            ],
            "config_file": "/opt/vertica/config/fullbk.ini",
            "config_script_base": "fullbk",
            "num_backups": 2
        }
    ],
    "href": "/backups",
    "mime-type": "application/vertica.databases.json-v2"
}

3.3.2.2 - POST backups/:config_script_base

为 VBR 配置脚本 :config_script_base 中定义的备份创建新的备份作业。VBR 配置脚本必须驻留在 /opt/vertica/configuration 中。:config_script_base 值不包含 .ini 文件名扩展名。

要确定有效的 :config_script_base 值,请参阅 GET backups

返回一个作业 ID,它可用于确定作业的状态。

资源 URL

https://<NODE>:5444/backups/:config_script_base

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "id": "CreateBackup-VMart-1404750602.03",
    "url": "/jobs/CreateBackup-VMart-1404750602.03"
}

3.3.2.3 - GET backups/:config_script_base/:archive_id

返回有关特定备份的详细信息。您必须提供 :config_script_base。此值为驻留在 /opt/vertica/config 中的 VBR config 文件(没有扩展名 .ini)的名称。:archive_idGET backups 命令返回的 backup 字段的值。

资源 URL

https://<NODE>:5444/backups/:config_script_base/:archive_id

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "archive_id": "v_vdb_bk_snapshot_20190304_204814",
    "config_file": "/opt/vertica/config/fullbk.ini",
    "objects": "",
    "href": "/backups/fullbk/v_vdb_bk_snapshot_20190304_204814",
    "exclude_patterns": "",
    "epoch": "16",
    "include_patterns": "",
    "backup_type": "full",
    "version": "v9.2.1-20190304",
    "hosts": "v_vdb_bk_node0001(10.20.91.240),
             v_vdb_bk_node0002(10.20.91.241),
             v_vdb_bk_node0003(10.20.91.242),
             v_vdb_bk_node0004(10.20.91.243),
             v_vdb_bk_node0005(10.20.91.244)"
}

3.3.2.4 - POST restore/:archive_id

创建新的恢复作业,以从 :archive_id 所标识的备份存档恢复数据库。:archive_idGET backups 命令返回的 backup 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/restore/:archive_id

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "id": "RestoreBackup-VMart-1404760113.71",
    "url": "/jobs/RestoreBackup-VMart-1404760113.71"
}

3.3.3 - 数据库

可以使用这些 API 调用与数据库交互。

3.3.3.1 - GET databases

返回数据库及其当前状态和数据库属性的列表。

资源 URL

https://<NODE>:5444/databases

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

使用 cURL 的完整请求的示例:

curl -H "VerticaApiKey: ValidAPIKey" https://<NODE>:5444/databases

响应:

{
    "body": [
        {
            "href": "/databases/VMart",
            "mime-type": [
                "application/vertica.database.json-v2"
            ],
            "name": "VMart",
            "port": "5433",
            "status": "UP"
        },
        {
            "href": "/databases/testDB",
            "mime-type": [
                "application/vertica.database.json-v2"
            ],
            "name": "testDB",
            "port": "5433",
            "status": "DOWN"
        }
    ],
    "href": "/databases",
    "links": [
        "/:database_name"
    ],
    "mime-type": "application/vertica.databases.json-v2"
}

3.3.3.2 - POST databases

创建一个作业,以使用所提供的参数创建新数据库。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases

<<<<<<<

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有管理级别的安全性。

API 密钥必须具有管理 级别的安全性。

参数

示例请求

响应:

{
    "jobid": "CreateDatabase-testDB-2014-07-07 15:49:53.219445",
    "resource": "/jobs/CreateDatabase-testDB-2014-07-07 15:49:53.219445",
    "userid": "dbadmin"
}

3.3.3.3 - GET databases/:database_name

返回特定数据库的详细信息。:database_nameGET databases 命令返回的 name 字段的值。

资源 URL

https://<NODE>:5444/databases/:database_name

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "body": {
        "database_id": "VMart",
        "id": "VMart",
        "nodes": "v_vmart_node0001,v_vmart_node0002,v_vmart_node0003",
        "nodes_new": [
            {
                "catalog_base": "/home/dbadmin",
                "data_base": "/home/dbadmin",
                "host": "10.20.100.247",
                "id": "v_vmart_node0001"
            },
            {
                "catalog_base": "/home/dbadmin",
                "data_base": "/home/dbadmin",
                "host": "10.20.100.248",
                "id": "v_vmart_node0002"
            },
            {
                "catalog_base": "/home/dbadmin",
                "data_base": "/home/dbadmin",
                "host": "10.20.100.249",
                "id": "v_vmart_node0003"
            }
        ],
        "path": "/home/dbadmin/VMart",
        "port": "5433",
        "restartpolicy": "ksafe",
        "status": "UP"
    },
    "href": "/databases/VMart",
    "links": [
        "/configuration",
        "/hosts",
        "/license",
        "/nodes",
        "/process",
        "/rebalance/process",
        "/status",
        "/Workload Analyzer/process"
    ],
    "mime-type": "application/vertica.database.json-v2"
}

3.3.3.4 - PUT databases/:database_name

创建一个作业,以对 :database_name 所标识的数据库运行 action 参数所标识的操作。:database_nameGET databases 命令返回的 name 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name

认证

请求头中需要包含 VerticaAPIKey

API 密钥的安全性级别必须为普通或更高。

参数

示例请求

响应:

{
    "id": "StopDatabase-testDB-2014-07-20 13:28:49.321744",
    "url": "/jobs/StopDatabase-testDB-2014-07-20 13:28:49.321744"
}

3.3.3.5 - DELETE databases/:database_name

创建一个作业,以在群集中删除(丢弃)现有数据库。若要执行此操作,您必须先停止该数据库。:database_nameGET databases 命令返回的 name 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有管理 级别的安全性。

参数

无。

示例请求

响应:

{
    "id": "DropDatabase-TestDB-2014-07-18 12:50:33.332383",
    "url": "/jobs/DropDatabase-TestDB-2014-07-18 12:50:33.332383"
}

3.3.3.6 - GET databases/:database_name/configuration

返回 :database_name 所标识的数据库的配置参数列表。:database_nameGET databases 命令返回的 name 字段的值。

资源 URL

https://<NODE>:5444/databases/:database_name/configuration

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

示例请求

响应:

此 API 调用将返回超过 100 个配置参数。以下响应只是返回的总数中的一小部分。

[
    {
        "node_name": "ALL",
        "parameter_name": "ACDAlgorithmForSynopsisVersion1",
        "current_value": "1",
        "restart_value": "1",
        "database_value": "1",
        "default_value": "1",
        "current_level": "DEFAULT",
        "restart_level": "DEFAULT",
        "is_mismatch": "f",
        "groups": "",
        "allowed_levels": "SESSION, DATABASE",
        "superuser_visible_only": "f",
        "change_under_support_guidance": "t",
        "change_requires_restart": "f",
        "description": "Algorithm used to interpret synopsis version 1 for approximate count distinct"
    },
    {
        "node_name": "ALL",
        "parameter_name": "ACDLinearCountThreshold",
        "current_value": "-1.000000",
        "restart_value": "-1.000000",
        "database_value": "-1.000000",
        "default_value": "-1.000000",
        "current_level": "DEFAULT",
        "restart_level": "DEFAULT",
        "is_mismatch": "f",
        "groups": "",
        "allowed_levels": "SESSION, DATABASE",
        "superuser_visible_only": "f",
        "change_under_support_guidance": "t",
        "change_requires_restart": "f",
        "description": "If positive, will overwrite the default linear counting threshold in approximate count distinct"
    },
    {
        "node_name": "ALL",
        "parameter_name": "ACDSynopsisVersion",
        "current_value": "2",
        "restart_value": "2",
        "database_value": "2",
        "default_value": "2",
        "current_level": "DEFAULT",
        "restart_level": "DEFAULT",
        "is_mismatch": "f",
        "groups": "",
        "allowed_levels": "SESSION, DATABASE",
        "superuser_visible_only": "f",
        "change_under_support_guidance": "t",
        "change_requires_restart": "f",
        "description": "Default synopsis version to be generated by approximate count distinct"
    },
    {
        "node_name": "ALL",
        "parameter_name": "AHMBackupManagement",
        "current_value": "0",
        "restart_value": "0",
        "database_value": "0",
        "default_value": "0",
        "current_level": "DEFAULT",
        "restart_level": "DEFAULT",
        "is_mismatch": "f",
        "groups": "",
        "allowed_levels": "NODE, DATABASE",
        "superuser_visible_only": "f",
        "change_under_support_guidance": "t",
        "change_requires_restart": "f",
        "description": "Consider backup epochs when setting new AHM"
    },
    {
        "node_name": "ALL",
        "parameter_name": "ARCCommitPercentage",
        "current_value": "3.000000",
        "restart_value": "3.000000",
        "database_value": "3.000000",
        "default_value": "3.000000",
        "current_level": "DEFAULT",
        "restart_level": "DEFAULT",
        "is_mismatch": "f",
        "groups": "",
        "allowed_levels": "DATABASE",
        "superuser_visible_only": "f",
        "change_under_support_guidance": "t",
        "change_requires_restart": "f",
        "description": "ARC will commit only if the change is more than the percentage specified"
    },
    {
        "node_name": "ALL",
        "parameter_name": "AWSCAFile",
        "current_value": "",
        "restart_value": "",
        "database_value": "",
        "default_value": "",
        "current_level": "DEFAULT",
        "restart_level": "DEFAULT",
        "is_mismatch": "f",
        "groups": "",
        "allowed_levels": "DATABASE",
        "superuser_visible_only": "f",
        "change_under_support_guidance": "f",
        "change_requires_restart": "f",
        "description": "Overrides the default CA file"
    },
    ...
]

3.3.3.7 - PUT databases/:database_name/configuration

:database_name 所标识的数据库设置一个或多个配置参数。:database_nameGET databases 命令返回的 name 字段的值。

返回参数名称、请求的值和尝试的更改的结果(Success 或 Failed)。

资源 URL

https://<NODE>:5444/databases/:database_name/configuration

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有管理级别的安全性。

参数

示例请求

响应:

[
    {
        "key": "JavaBinaryForUDx",
        "result": "Success",
        "value": "/usr/bin/java"
    },
    {
        "key": "TransactionIsolationLevel",
        "result": "Success",
        "value": "SERIALIZABLE"
    }
]

3.3.3.8 - GET databases/:database_name/hosts

返回与 :database_name 所标识的数据库关联的每个主机的主机名/IP 地址、节点名称和 UP/DOWN 状态。:database_nameGET databases 命令返回的 name 字段的值。

资源 URL

https://<NODE>:5444/databases/:database_name/hosts

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "body": [
        {
            "hostname": "10.20.100.247",
            "nodename": "v_vmart_node0001",
            "status": "UP",
            "ts": "2014-07-18T13:12:31.904191"
        },
        {
            "hostname": "10.20.100.248",
            "nodename": "v_vmart_node0002",
            "status": "UP",
            "ts": "2014-07-18T13:12:31.904209"
        },
        {
            "hostname": "10.20.100.249",
            "nodename": "v_vmart_node0003",
            "status": "UP",
            "ts": "2014-07-18T13:12:31.904215"
        }
    ],
    "href": "/databases/VMart/hosts",
    "links": [],
    "mime-type": "application/vertica.hosts.json-v2"
}

3.3.3.9 - POST databases/:database_name/hosts

创建一个作业,以将主机添加到 :database_name 所标识的数据库。此主机必须已属于该群集的一部分。:database_nameGET databases 命令返回的 name 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name/hosts

<<<<<<<

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有管理级别的安全性。

API 密钥必须具有管理 级别的安全性。

参数

示例请求

响应:

{
    "id": "AddHostToDatabase-testDB-2014-07-20 12:24:04.088812",
    "url": "/jobs/AddHostToDatabase-testDB-2014-07-20 12:24:04.088812"
}

3.3.3.10 - DELETE databases/:database_name/hosts/:host_id

创建一个作业,以从 :database_name 所标识的数据库中移除 :host_id 所标识的主机。:database_nameGET databases 命令返回的 name 字段的值。:host_idGET databases/:database_name 返回的 host 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name/hosts/:host_id

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有管理 级别的安全性。

参数

示例请求

响应:

{
    "id": "RemoveHostFromDatabase-testDB-2014-07-20 13:41:15.646235",
    "url": "/jobs/RemoveHostFromDatabase-testDB-2014-07-20 13:41:15.646235"
}

3.3.3.11 - POST databases/:database_name/hosts/:host_id/process

创建一个作业,以在 :host_id 所标识的主机上启动 :database_name 所标识的数据库的 Vertica 进程。:database_nameGET databases 命令返回的 name 字段的值。:host_idGET databases/:database_name 返回的 host 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name/hosts/:host_id/process

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "id": "StartDatabase-testDB-2014-07-20 13:14:03.968340",
    "url": "/jobs/StartDatabase-testDB-2014-07-20 13:14:03.968340"
}

3.3.3.12 - GET databases/:database_name/license

返回有关 :database_name 所标识的数据库正在使用的数据库许可证的详细信息。:database_nameGET databases 命令返回的 name 字段的值。

资源 URL

https://<NODE>:5444/:database_name/license

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

示例请求

响应:

{
    "body": {
        "details": {
            "assigned_to": "Vertica Systems, Inc.",
            "grace_period": 0,
            "is_ce": false,
            "is_unlimited": false,
            "name": "vertica",
            "not_after": "Perpetual",
            "not_before": "2007-08-03"
        },
        "last_audit": {
            "audit_date": "2014-07-18 13:49:22.530105-04",
            "database_size_bytes": "814060522",
            "license_size_bytes": "536870912000",
            "usage_percent": "0.00151630588248372"
        }
    },
    "href": "/databases/VMart/license",
    "links": [],
    "mime-type": "application/vertica.license.json-v2"
}

3.3.3.13 - GET databases/:database_name/licenses

返回有关 :database_name 所标识的数据库正在使用的所有许可证的详细信息。:database_nameGET databases 命令返回的 name 字段的值。

资源 URL

https://<NODE>:5444/:database_name/licenses

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

示例请求

响应:

{
    "body": [
        {
            "details": {
                "assigned_to": "Vertica Systems, Inc.",
                "audit_date": "2014-07-19 21:35:25.111312",
                "is_ce": "False",
                "name": "vertica",
                "node_restriction": "",
                "not_after": "Perpetual",
                "not_before": "2007-08-03",
                "size": "500GB"
            },
            "last_audit": {
                "audit_date": "2014-07-19 21:35:26.318378-04",
                "database_size_bytes": "819066288",
                "license_size_bytes": "536870912000",
                "usage_percent": "0.00152562984824181"
            }
        },
        {
            "details": {
                "assigned_to": "Vertica Systems, Inc., FlexTable",
                "audit_date": "2014-07-19 21:35:25.111312",
                "is_ce": "False",
                "name": "com.vertica.flextable",
                "node_restriction": "",
                "not_after": "Perpetual",
                "not_before": "2007-08-03",
                "size": "500GB"
            },
            "last_audit": {
                "audit_date": "2014-07-19 21:35:25.111312",
                "database_size_bytes": 0,
                "license_size_bytes": 536870912000,
                "usage_percent": 0
            }
        }
    ],
    "href": "/databases/VMart/licenses",
    "links": [],
    "mime-type": "application/vertica.features.json-v2"
}

3.3.3.14 - DELETE databases/:database_name/hosts/:host_id/process

创建一个作业,以在 :host_id 所标识的主机上停止 :database_name 所标识的数据库的 Vertica 进程。:database_nameGET databases 命令返回的 name 字段的值。:host_idGET databases/:database_name 返回的 host 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name/hosts/:host_id/process

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "id": "StopDatabase-testDB-2014-07-20 13:02:08.453547",
    "url": "/jobs/StopDatabase-testDB-2014-07-20 13:02:08.453547"
}

3.3.3.15 - POST databases/:database_name/hosts/:host_id/replace_with/:host_id_new

创建一个作业以替换由 hosts/:host_id 标识的主机,其中 replace_with/:host_id. Vertica performs these operations for the database identified by :database_name. The :database_name 标识的主机是 GET databases 命令返回的 name 字段的值。:host_idGET databases/:database_name 返回的 host 字段的值。可以使用 GET hosts 查找有效的替换主机。替换主机不能已属于该数据库的一部分。必须在要替换的主机上停止 Vertica 进程。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name/hosts/:host_id/replace_with/:host_id_new

<<<<<<<

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有管理级别的安全性。

API 密钥必须具有管理 级别的安全性。

参数

示例请求

响应:

{
    "id": "ReplaceNode-testDB-2014-07-20 13:50:28.423509",
    "url": "/jobs/ReplaceNode-testDB-2014-07-20 13:50:28.423509"
}

3.3.3.16 - GET databases/:database_name/nodes

返回 :database_name 所标识的数据库的节点 ID 的逗号分隔列表。:database_nameGET databases 命令返回的 name 字段的值。

资源 URL

https://<NODE>:5444/:database_name/nodes

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

[
    {
        "database_id": "VMart",
        "node_id": "v_vmart_node0001,v_vmart_node0002,v_vmart_node0003",
        "status": "Unknown"
    }
]

3.3.3.17 - GET databases/:database_name/nodes/:node_id

返回 :node_id 标识的节点的详细信息。:node_idGET databases/:database_name/nodes 返回的节点 ID 之一。

资源 URL

https://<NODE>:5444/:database_name/nodes/:node_id

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "db": "VMart",
    "host": "10.20.100.247",
    "name": "v_vmart_node0001",
    "state": "UP"
}

3.3.3.18 - POST databases/:database_name/process

创建一个作业,以启动 :database_name 所标识的数据库。:database_nameGET databases 命令返回的 name 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name/process

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

示例请求

使用 cURL 的完整请求的示例:

curl -d "epoch=epoch_number&include=host1,host2" -X POST -H "VerticaApiKey: ValidAPIKey" https://<NODE>:5444/:testDB/process

响应:

{
    "id": "StartDatabase-testDB-2014-07-20 12:41:46.061408",
    "url": "/jobs/StartDatabase-testDB-2014-07-20 12:41:46.061408"
}

3.3.3.19 - GET databases/:database_name/process

返回 :database_name 所标识的数据库的 UP 或 DOWN 状态。:database_nameGET databases 命令返回的 name 字段的值。

资源 URL

https://<NODE>:5444/databases/:database_name/process

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "state": "UP"
}

3.3.3.20 - DELETE databases/:database_name/process

创建一个作业,以停止 :database_name 所标识的数据库。:database_nameGET databases 命令返回的 name 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name/process

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

示例请求

使用 cURL 的完整请求的示例:

curl -X DELETE -H "VerticaApiKey: ValidAPIKey" https://<NODE>:5444/:testDB/process?user_id=dbadmin"&"passwd=vertica

响应:

{
    "id": "StopDatabase-testDB-2014-07-20 12:46:04.406637",
    "url": "/jobs/StopDatabase-testDB-2014-07-20 12:46:04.406637"
}

3.3.3.21 - POST databases/:database_name/rebalance/process

创建一个作业,以对 :database_name 所标识的数据库运行重新平衡。:database_nameGET databases 命令返回的 name 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name/rebalance/process

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

示例请求

响应:

{
    "id": "RebalanceData-testDB-2014-07-20 21:42:45.731038",
    "url": "/jobs/RebalanceData-testDB-2014-07-20 21:42:45.731038"
}

3.3.3.22 - POST databases/:database_name/Workload analyzer/process

创建一个作业,以对 :database_name 所标识的数据库运行 Workload Analyzer。:database_nameGET databases 命令返回的 name 字段的值。

返回一个作业 ID,它可用于确定作业的状态。请参阅GET jobs

资源 URL

https://<NODE>:5444/databases/:database_name/Workload Analyzer/process

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

示例请求

响应:

{
    "id": "AnalyzeWorkLoad-testDB-2014-07-20 21:48:27.972989",
    "url": "/jobs/AnalyzeWorkLoad-testDB-2014-07-20 21:48:27.972989"
}

3.3.4 - 主机

可以使用这些 API 调用获取有关群集中的主机的信息。

3.3.4.1 - GET hosts

返回群集中的主机的列表,以及有关每个主机的硬件、软件和网络详细信息。

资源 URL

https://<NODE>:5444/hosts

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "body": [
        {
            "cpu_info": {
                "cpu_type": " Intel(R) Xeon(R) CPU E5-2695 v2 @ 2.40GHz",
                "number_of_cpus": 2
            },
            "host_id": "10.20.100.247",
            "hostname": "v_vmart_node0001.example.com",
            "max_user_proc": "3833",
            "nics": [
                {
                    "broadcast": "10.20.100.255",
                    "ipaddr": "10.20.100.247",
                    "name": "eth0",
                    "netmask": "255.255.255.0",
                    "speed": "unknown"
                },
                {
                    "broadcast": "255.255.255.255",
                    "ipaddr": "127.0.0.1",
                    "name": "lo",
                    "netmask": "255.0.0.0",
                    "speed": "locallink"
                }
            ],
            "total_memory": 3833,
            "vertica": {
                "arch": "x86_64",
                "brand": "vertica",
                "release": "20140716",
                "version": "12.0.x0"
            }
        },
        {
            "cpu_info": {
                "cpu_type": " Intel(R) Xeon(R) CPU E5-2695 v2 @ 2.40GHz",
                "number_of_cpus": 2
            },
            "host_id": "10.20.100.248",
            "hostname": "v_vmart_node0002.example.com",
            "max_user_proc": "3833",
            "nics": [
                {
                    "broadcast": "10.20.100.255",
                    "ipaddr": "10.20.100.248",
                    "name": "eth0",
                    "netmask": "255.255.255.0",
                    "speed": "unknown"
                },
                {
                    "broadcast": "255.255.255.255",
                    "ipaddr": "127.0.0.1",
                    "name": "lo",
                    "netmask": "255.0.0.0",
                    "speed": "locallink"
                }
            ],
            "total_memory": 3833,
            "vertica": {
                "arch": "x86_64",
                "brand": "vertica",
                "release": "20140716",
                "version": "12.0.x0"
            }
        },
        {
            "cpu_info": {
                "cpu_type": " Intel(R) Xeon(R) CPU E5-2695 v2 @ 2.40GHz",
                "number_of_cpus": 2
            },
            "host_id": "10.20.100.249",
            "hostname": "v_vmart_node0003.example.com",
            "max_user_proc": "3833",
            "nics": [
                {
                    "broadcast": "10.20.100.255",
                    "ipaddr": "10.20.100.249",
                    "name": "eth0",
                    "netmask": "255.255.255.0",
                    "speed": "unknown"
                },
                {
                    "broadcast": "255.255.255.255",
                    "ipaddr": "127.0.0.1",
                    "name": "lo",
                    "netmask": "255.0.0.0",
                    "speed": "locallink"
                }
            ],
            "total_memory": 3833,
            "vertica": {
                "arch": "x86_64",
                "brand": "vertica",
                "release": "20140716",
                "version": "12.0.x0"
            }
        }
    ],
    "href": "/hosts",
    "links": [
        "/:hostid"
    ],
    "mime-type": "application/vertica.hosts.json-v2"
}

3.3.4.2 - GET hosts/:hostid

返回有关 :host_id 所标识的主机的硬件、软件和网络详细信息。可以使用 GET hosts 查找每个主机的 :host_id

资源 URL

https://<NODE>:5444/hosts/:hostid

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "body": {
        "cpu_info": {
            "cpu_type": " Intel(R) Xeon(R) CPU E5-2695 v2 @ 2.40GHz",
            "number_of_cpus": 2
        },
        "hostname": "v_vmart_node0001.example.com",
        "max_user_proc": "3833",
        "nics": [
            {
                "broadcast": "10.20.100.255",
                "ipaddr": "10.20.100.247",
                "name": "eth0",
                "netmask": "255.255.255.0",
                "speed": "unknown"
            },
            {
                "broadcast": "255.255.255.255",
                "ipaddr": "127.0.0.1",
                "name": "lo",
                "netmask": "255.0.0.0",
                "speed": "locallink"
            }
        ],
        "total_memory": 3833,
        "vertica": {
            "arch": "x86_64",
            "brand": "vertica",
            "release": "20140716",
            "version": "12.0.x0"
        }
    },
    "href": "/hosts/10.20.100.247",
    "links": [],
    "mime-type": "application/vertica.host.json-v2"
}

3.3.5 - 作业

可以使用这些 API 调用获取有关数据库的作业的信息。

3.3.5.1 - GET jobs

返回代理正在跟踪的作业的列表和作业详细信息。

作业始终立即启动。is_running 字段是一个布尔值。如果 is_running 为 false,则表明作业已完成。

exit_code 详细介绍了作业的状态。某些类型的作业的 exit_code 是不同的:

  • 对于备份作业:

    • 0 表示成功。

    • 所有其他数字都表示失败。

  • 对于所有其他作业:

    • -9 表示成功。

    • 所有其他数字都表示失败。

可以在 /opt/vertica/log/agentStdMsg.log 中查看有关失败的详细信息。

资源 URL

https://<NODE>:5444/jobs

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "body": [
        {
            "exit_code": 0,
            "id": "CreateBackup-VMart-1405012447.75",
            "is_running": false,
            "status": "unused",
            "ts": "1405012461.18"
        },
        {
            "exit_code": 1,
            "id": "CreateBackup-VMart-1405012454.88",
            "is_running": false,
            "status": "unused",
            "ts": "1405012455.18"
        }
    ],
    "href": "/jobs",
    "links": [
        "/:jobid"
    ],
    "mime-type": "application/vertica.jobs.json-v2"
}

3.3.5.2 - GET jobs/:id

使用所提供的 :id 获取特定作业的详细信息。可以使用 GET jobs 确定 jobs/:id 的列表。

特定作业的详细信息与 GET jobs 为所有作业提供的详细信息相同。

资源 URL

https://<NODE>:5444/jobs/:id

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

3.3.6 - 许可证

可以使用这些 API 调用管理数据库的许可证。

3.3.6.1 - POST licenses

将许可证文件上传并应用到此群集。

必须以 HTTP POST 表单上传格式提供许可证文件,并用名称 license 标识该许可证文件。例如,可以使用以下 cURL:

curl -k --request POST -H "VerticaApiKey:ValidAPIKey" \
https://v_vmart_node0001:5444/licenses --form "license=@vlicense.dat"

资源 URL

https://<NODE>:5444/licenses

<<<<<<<

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有管理级别的安全性。

API 密钥必须具有管理 级别的安全性。

参数

无。

示例请求

响应:

成功的上传没有 HTTP 主体响应。成功的上传将返回 HTTP 200/OK 标头。

3.3.6.2 - GET licenses

返回在创建数据库时由此群集使用的任何许可证文件。许可证文件必须驻留在 /opt/vertica/config/share 中。

资源 URL

https://<NODE>:5444/licenses

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "body": [
        {
            "comment": "Vertica license is valid",
            "end": "Perpetual",
            "grace": "0",
            "size": "1TB CE Nodes 3",
            "start": "2011-11-22",
            "status": true,
            "vendor": "Vertica Community Edition"
        }
    ],
    "href": "/license",
    "links": [],
    "mime-type": "application/vertica.license.json-v2"
}

3.3.7 - 节点

可以使用这些 API 调用检索有关群集中的节点的信息。

3.3.7.1 - GET nodes

返回与此群集关联的节点的列表。

资源 URL

https://<NODE>:5444/nodes

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "body": [
        "node0001",
        "node0002",
        "node0003",
        "v_testdb_node0001",
        "v_testdb_node0002",
        "v_testdb_node0003",
        "v_vmart_node0001",
        "v_vmart_node0002",
        "v_vmart_node0003"
    ],
    "href": "/nodes",
    "links": [
        "/:nodeid"
    ],
    "mime-type": "application/vertica.nodes.json-v2"
}

3.3.7.2 - GET nodes/:nodeid

返回 :node_id 标识的节点的详细信息。可以使用 GET nodes 查找每个节点的 :node_id

主体字段中以逗号分隔格式详细介绍了以下信息:

  • 节点名称

  • 主机地址

  • 编录目录

  • 数据目录

资源 URL

https://<NODE>:5444/nodes/:node_id

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "body": [
        "v_vmart_node0001",
        "10.20.100.247,/home/dbadmin,/home/dbadmin"
    ],
    "href": "/nodes/v_vmart_node0001",
    "links": [],
    "mime-type": "application/vertica.node.json-v2"
}

3.3.8 - Webhooks

可以使用这些 API 调用获取有关 Webhook 的信息,以及创建或删除 Webhook。

3.3.8.1 - GET webhooks

返回此群集的活动 Webhook 的列表。

资源 URL

https://<NODE>:5444/webhooks

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

{
    "body": [
        {
            "host": "192.168.232.1",
            "id": "79c1c8a18be02804b3d2f48ea6462909",
            "port": 80,
            "timestamp": "2014-07-20 22:54:09.829642",
            "url": "/gettest.htm"
        },
        {
            "host": "192.168.232.1",
            "id": "9c32cb0f3d2f9a7cb10835f1732fd4a7",
            "port": 80,
            "timestamp": "2014-07-20 22:54:09.829707",
            "url": "/getwebhook.php"
        }
    ],
    "href": "/webhooks",
    "links": [
        "/subscribe",
        "/:subscriber_id"
    ],
    "mime-type": "application/vertica.webhooks.json-v2"
}

3.3.8.2 - POST webhooks/subscribe

为 Webhook 创建订阅。

资源 URL

https://<NODE>:5444/webhooks/subscribe

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

示例请求

响应:

响应不采用 JSON 编码。仅有的文本响应是 Webhook 订阅的 ID。此外,HTTP 200/OK 标头指示操作成功。

79c1c8a18be02804b3d2f48ea6462909

3.3.8.3 - DELETE webhooks/:subscriber_id

删除 :subscriber_id 所标识的 Webhook。:subscriber_idGET webhooks 命令返回的 id 字段的值。

资源 URL

https://<NODE>:5444/webhooks/:subscriber_id

认证

请求头中需要包含 VerticaAPIKey

API 密钥必须具有受限 级别或更高级别的安全性。

参数

无。

示例请求

响应:

成功的删除没有 HTTP 主体响应。成功的删除将返回 HTTP 200/OK 标头。

3.4 - 管理控制台的 Rest API

这些 API 调用与管理控制台节点交互。

警报

时间信息

3.4.1 - MC-User-ApiKey

MC-User-ApiKey 是特定于用户的密钥,与管理控制台结合使用。用户必须拥有 MC-User-ApiKey 才能使用 Rest API 与 MC 交互。所有具有 None 以外的其他角色的用户都将自动收到 MC-User-ApiKey。

此密钥可通过 API 向用户授予他们已通过 MC 角色获取的相同权限。若要与 MC 交互,用户应在 API 的请求标头中传递该密钥。

查看 MC-User-ApiKey

如果您是数据库管理员,则可以查看所有用户的 MC-User-ApiKey。各个用户可以查看各自的密钥。

  1. 连接到 MC,然后转到“MC 设置 (MC Settings)”>“用户管理 (User Management)”。

  2. 选择要查看的用户,然后单击“编辑 (Edit)”。该用户的密钥将显示在“用户 API 密钥 (User API Key)”字段中。

3.4.2 - GET alerts

返回 MC 警报及其当前状态和数据库属性的列表。

资源 URL

https://<MC_NODE>:5450/webui/api/alerts

认证

请求头中需要包含 MC-User-Apikey

筛选器参数

示例请求

使用 cURL 请求警报

以下示例显示了如何使用 cURL 请求警报。在此示例中,limit 参数设置为“2”,而 types 参数设置为 infonotice

curl -H "MC-User-ApiKey: ValidUserKey" https://<MC_NODE>:5450/webui/api/alerts?limit=2&types=info,notice

响应:

[
   {
      "alerts":[
         {
            "id":5502,
            "markedRead":false,
            "eventTypeCode":0,
            "create_time":"2016-02-02 05:12:10.0",
            "updated_time":"2016-02-02 15:50:20.511",
            "severity":"warning",
            "status":1,
            "nodeName":"v_vmart_node0001",
            "databaseName":"VMart",
            "databaseId":1,
            "clusterName":"1449695416208_cluster",
            "description":"Warning: Low disk space detected (73% in use)",
            "summary":"Low Disk Space",
            "internal":false,
            "count":3830
         },
         {
            "id":5501,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2016-02-02 05:12:02.31",
            "updated_time":"2016-02-02 05:12:02.31",
            "severity":"notice",
            "status":1,
            "databaseName":"VMart",
            "databaseId":1,
            "clusterName":"1449695416208_cluster",
            "description":"Analyze Workload operation started on Database",
            "summary":"Analyze Workload operation started on Database",
            "internal":false,
            "count":1
         }
      ],
      "total_alerts":190,
      "request_query":"limit=2",
      "request_time":"2016-02-02 15:50:26 -0500"
   }
]

请求某个时间范围内的警报

以下示例显示了多种方法,您可以使用这些方法请求与以上示例相同但处于指定时间范围的警报。

使用 time_fromtime_to 参数请求处于特定时间范围的警报:

curl -H "MC-User-ApiKey: ValidUserKey" https://<MC_NODE>:5450/webui/api/alerts?types=info,notice&time_from=2016-01-01T12:12&time_to=2016-02-01T12:12

使用 time_from 参数请求从特定开始时间到当前时间的警报:

curl -H "MC-User-ApiKey: ValidUserKey" https://<MC_NODE>:5450/webui/api/alerts?types=info,notice&time_from=2016-01-01T12:12

使用 time_to 参数请求到特定终点为止的警报:如果使用 time_to 参数但不使用 time_from 参数,则 time_from 参数默认设置为 MC 所包含的最旧警报:

curl -H "MC-User-ApiKey: ValidUserKey" https://<MC_NODE>:5450/webui/api/alerts?types=info,notice&time_to=2016-01-01T12:12

3.4.3 - GET mcTimeInfo

返回 MC 服务器的当前时间和该 MC 服务器所位于的时区。

资源 URL

https://<MC_NODE>:5450/webui/api/mcTimeInfo

认证

请求头中需要包含 MC-User-Apikey

参数

无。

示例请求

以下示例显示了如何使用 cURL 请求 MC 时间信息:

curl -H "MC-User-ApiKey: ValidUserKey" https://<MC_NODE>:5450/webui/api/mcTimeInfo

响应:

{"mc_current_time":"Tue, 2000-01-01 01:02:03 -0500","mc_timezone":"US/Eastern"}

3.4.4 - 阈值类别筛选器

返回与 MC 中的阈值设置相关的警报的列表。

资源 URL

https://<MC_NODE>:5450/webui/api/alerts?category=thresholds

认证

请求头中需要包含 MC-User-Apikey

示例请求

以下示例显示了如何使用 cURL 请求有关阈值的警报:

curl -H "MC-User-ApiKey: ValidUserKey" https://<MC_NODE>:5450/webui/api/alerts?category=thresholds

响应:


 [
   {
      "alerts":[
         {
            "id":33,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-10 10:28:41.332",
            "updated_time":"2015-11-10 10:28:41.332",
            "severity":"warning",
            "status":1,
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":" Database: mydb Lower than threshold Node Disk I/O 10 %   v_mydb_node0002 ;1.6%  v_mydb_node0002 ;1.4%  v_mydb_node0002 ;2.3%  v_mydb_node0002 ;1.13%  v_mydb_node0002 ;1.39%  v_mydb_node0001 ;3.78%  v_mydb_node0003 ;1.79%  ",
            "summary":"Threshold : Node Disk I/O < 10 %",
            "internal":false,
            "count":1
         },
         {
            "id":32,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-10 10:28:40.975",
            "updated_time":"2015-11-10 10:28:40.975",
            "severity":"warning",
            "status":1,
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":" Database: mydb Lower than threshold Node Memory 10 %   v_mydb_node0002 ;5.47%  v_mydb_node0002 ;5.47%  v_mydb_node0002 ;5.47%  v_mydb_node0002 ;5.47%  v_mydb_node0002 ;5.48%  v_mydb_node0003 ;4.53%  ",
            "summary":"Threshold : Node Memory < 10 %",
            "internal":false,
            "count":1
         },
         {
            "id":31,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-10 10:28:40.044",
            "updated_time":"2015-11-10 10:28:40.044",
            "severity":"warning",
            "status":1,
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":" Database: mydb Lower than threshold Node CPU 10 %   v_mydb_node0002 ;1.4%  v_mydb_node0002 ;1.64%  v_mydb_node0002 ;1.45%  v_mydb_node0002 ;2.49%  ",
            "summary":"Threshold : Node CPU < 10 %",
            "internal":false,
            "count":1
         },
         {
            "id":30,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-10 10:28:34.562",
            "updated_time":"2015-11-10 10:28:34.562",
            "severity":"warning",
            "status":1,
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":" Database: mydb Exceed threshold Node Disk Usage 60 %   v_mydb_node0001 ;86.41%  ",
            "summary":"Threshold : Node Disk Usage > 60 %",
            "internal":false,
            "count":1
         }
      ],
      "total_alerts":4,
      "request_query":"category=thresholds",
      "request_time":"2015-11-10 10:29:17.129"
   }
]

另请参阅

3.4.5 - 数据库名称类别筛选器

返回有关特定数据库的 MC 警报的列表。

资源 URL

https://<MC_NODE>:5450/webui/api/alerts?db_name=

认证

请求头中需要包含 MC-User-Apikey

示例请求

以下示例显示了如何使用 cURL 查看有关特定数据库的警报:

curl -H "MC-User-ApiKey: ValidUserKey" https://<MC_NODE>:5450/webui/api/alerts?db_name="mydb"

响应:

[
   {
      "alerts":[
         {
            "id":9,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-05 15:10:53.391",
            "updated_time":"2015-11-05 15:10:53.391",
            "severity":"notice",
            "status":1,
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":"Workload analyzed successfully",
            "summary":"Analyze Workload operation has succeeded on Database",
            "internal":false,
            "count":1
         },
         {
            "id":8,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-05 15:10:31.16",
            "updated_time":"2015-11-05 15:10:31.16",
            "severity":"notice",
            "status":1,
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":"Analyze Workload operation started on Database",
            "summary":"Analyze Workload operation started on Database",
            "internal":false,
            "count":1
         },
         {
            "id":7,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-05 00:15:00.204",
            "updated_time":"2015-11-05 00:15:00.204",
            "severity":"alert",
            "status":1,
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":"Workload analyzed successfully",
            "summary":"Analyze Workload operation has succeeded on Database",
            "internal":false,
            "count":1
         },
         {
            "id":6,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-04 15:14:59.344",
            "updated_time":"2015-11-04 15:14:59.344",
            "severity":"notice",
            "status":1,
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":"Workload analyzed successfully",
            "summary":"Analyze Workload operation has succeeded on Database",
            "internal":false,
            "count":1
         },
         {
            "id":5,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-04 15:14:38.925",
            "updated_time":"2015-11-04 15:14:38.925",
            "severity":"notice",
            "status":1,
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":"Analyze Workload operation started on Database",
            "summary":"Analyze Workload operation started on Database",
            "internal":false,
            "count":1
         },
         {
            "id":4,
            "markedRead":false,
            "eventTypeCode":0,
            "create_time":"2015-11-04 15:14:33.0",
            "updated_time":"2015-11-05 16:26:17.978",
            "severity":"notice",
            "status":1,
            "nodeName":"v_mydb_node0001",
            "databaseName":"lmydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":"Workload analyzed successfully",
            "summary":"Analyze Workload operation has succeeded on Database",
            "internal":false,
            "count":1
         },
         {
            "id":3,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-04 15:14:32.806",
            "updated_time":"2015-11-04 15:14:32.806",
            "severity":"info",
            "status":1,
            "hostIp":"10.20.100.64",
            "nodeName":"v_mydb_node0003",
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":"Agent status is UP on IP 127.0.0.1",
            "summary":"Agent status is UP on IP 127.0.0.1",
            "internal":false,
            "count":1
         },
         {
            "id":2,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-04 15:14:32.541",
            "updated_time":"2015-11-04 15:14:32.541",
            "severity":"info",
            "status":1,
            "hostIp":"10.20.100.63",
            "nodeName":"v_mydb_node0002",
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":"Agent status is UP on IP 127.0.0.1",
            "summary":"Agent status is UP on IP 127.0.0.1",
            "internal":false,
            "count":1
         },
         {
            "id":1,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-04 15:14:32.364",
            "updated_time":"2015-11-04 15:14:32.364",
            "severity":"info",
            "status":1,
            "hostIp":"10.20.100.62",
            "nodeName":"v_mydb_node0001",
            "databaseName":"mydb",
            "databaseId":1,
            "clusterName":"1446668057043_cluster",
            "description":"Agent status is UP on IP 127.0.0.1",
            "summary":"Agent status is UP on IP 127.0.0.1",
            "internal":false,
            "count":1
         }
      ],
      "total_alerts":9,
      "request_query":"db_name=mydb",
      "request_time":"2015-11-05 16:26:21.679"
   }
]

3.4.6 - 将子类别筛选器与类别筛选器组合起来

您可以将类别筛选器与子类别筛选器结合使用,以获取在 MC 中设置的特定阈值的警报消息。还可以使用子类别筛选器,以获取有关数据库中特定资源池的警报。

子类别筛选器

可以将以下子类别筛选器与类别筛选器结合使用。子类别筛选器区分大小写,并且必须是小写。

特定于资源池的子类别筛选器

若要检索特定资源池的警报,您可以将子类别筛选器与以下类别筛选器结合使用:

  • thresholds

  • rp_name

如果使用这些子类别筛选器但不使用 RP_NAME 筛选器,查询将检索数据库中所有资源池的警报。

认证

请求头中需要包含 MC-User-Apikey

示例请求

将阈值类别筛选器与子类别筛选器结合使用

以下示例显示了如何将 cURL 与阈值类别筛选器和子类别筛选器结合使用以获取警报。您可应用以下筛选器:

  • THRESHOLDS

  • THRESHOLD_NODE_CPU

curl -H "MC-User-ApiKey: ValidUserKey" https://<MC_NODE>:5450/webui/api/alerts?category=thresholds&subcategory=threshold_node_cpu

响应:

[
   {
      "alerts":[
         {
            "id":11749,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-05 11:04:43.997",
            "updated_time":"2015-11-05 11:04:43.997",
            "severity":"warning",
            "status":1,
            "databaseName":"mydb",
            "databaseId":105,
            "clusterName":"1443122180317_cluster",
            "description":" Database: mydb Lower than threshold Node CPU 10 %   v_mydb_node0002 ;1.03%  v_mydb_node0003 ;0.9%  v_mydb_node0001 ;1.36%  ",
            "summary":"Threshold : Node CPU < 10 %",
            "internal":false,
            "count":1
         },
         {
            "id":11744,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-05 10:59:46.107",
            "updated_time":"2015-11-05 10:59:46.107",
            "severity":"warning",
            "status":1,
            "databaseName":"mydb2",
            "databaseId":106,
            "clusterName":"1443552354071_cluster",
            "description":" Database: mydb2 Lower than threshold Node CPU 10 %   v_mydb2_node0002 ;0.83%  v_mydb2_node0001 ;1.14%  ",
            "summary":"Threshold : Node CPU < 10 %",
            "internal":false,
            "count":1
         }
      ],
      "total_alerts":2,
      "request_query":"category=thresholds&subcategory=threshold_node_cpu",
      "request_time":"2015-11-05 11:05:28.116"
   }
]

请求有关特定资源池的警报

以下示例显示了如何使用 cURL 请求有关特定资源池的警报。资源池的名称是 resourcepool1。您可应用以下筛选器:

  • THRESHOLDS

  • RP_NAME

  • THRESHOLD_RP_QUERY_RUN_TIME

curl -H "MC-User-ApiKey: ValidUserKey" https://<MC_NODE>:5450/webui/api/alerts?category=thresholds&subcategory=threshold_rp_query_run_time&rp_name=resourcepool1

响应:

[
   {
      "alerts":[
         {
            "id":6525,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-05 14:25:36.797",
            "updated_time":"2015-11-05 14:25:36.797",
            "severity":"warning",
            "status":1,
            "databaseName":"mydb",
            "databaseId":106,
            "clusterName":"1443552354071_cluster",
            "description":" Resource Pool: resourcepool1  Threshold Name: Ended Query with Run Time Exceeding Limit  Time Interval: 14:20:36 to 14:25:36  Threshold Value: 0 min(s)  Actual Value: 2186 query(s) ",
            "summary":"Resource Pool: resourcepool1; Threshold : Ended Query with Run Time Exceeding Limit > 0 min(s)",
            "internal":false,
            "count":1
         },
         {
            "id":6517,
            "markedRead":false,
            "eventTypeCode":2,
            "create_time":"2015-11-05 14:20:39.541",
            "updated_time":"2015-11-05 14:20:39.541",
            "severity":"warning",
            "status":1,
            "databaseName":"mydb",
            "databaseId":106,
            "clusterName":"1443552354071_cluster",
            "description":" Resource Pool: resourcepool1  Threshold Name: Ended Query with Run Time Exceeding Limit  Time Interval: 14:15:39 to 14:20:39  Threshold Value: 0 min(s)  Actual Value: 2259 query(s) ",
            "summary":"Resource Pool: resourcepool1; Threshold : Ended Query with Run Time Exceeding Limit > 0 min(s)",
            "internal":false,
            "count":1
         }
      ],
      "total_alerts":14,
      "request_query":"category=thresholds&subcategory=threshold_rp_query_run_time&rp_name=resourcepool1",
      "request_time":"2015-11-05 11:07:43.988"
   }
]