simpod/clickhouse-client

PHP ClickHouse客户端

0.7.6 2024-05-13 09:55 UTC

README

Build Status Code Coverage Downloads Infection MSI

动机

该库尽量不隐藏任何ClickHouse HTTP接口的特定细节。也就是说,一切都是尽可能透明的,并且提供了面向对象的API,而不发明自己的抽象。
这里使用的命名与ClickHouse文档中的相同。

内容

设置

composer require simpod/clickhouse-client  
  1. 阅读有关ClickHouse Http接口 的内容。 它很短,但有助于理解概念。
  2. 创建一个新的ClickHouse客户端实例,并传递PSR工厂。
    1. 建议使用Symfony HttpClient(性能,更少错误,维护)
    2. 这个转折点是,这个库中没有配置端点/凭据等,而是通过客户端提供。
  3. 查看测试
<?php

use Http\Client\Curl\Client;
use Nyholm\Psr7\Factory\Psr17Factory;
use SimPod\ClickHouseClient\Client\PsrClickHouseClient;
use SimPod\ClickHouseClient\Client\Http\RequestFactory;

$psr17Factory = new Psr17Factory;

$clickHouseClient = new PsrClickHouseClient(
    new Client(),
    new RequestFactory(
        $psr17Factory,
        $psr17Factory
    ),
    [],
    new DateTimeZone('UTC')
);

Symfony HttpClient示例

配置HTTP客户端

如ClickHouse HTTP接口规范中所述,我们使用头部进行认证,例如通过查询设置默认数据库。

framework:
    http_client:
        scoped_clients:
            click_house.client:
                base_uri: '%clickhouse.endpoint%'
                headers:
                    'X-ClickHouse-User': '%clickhouse.username%'
                    'X-ClickHouse-Key': '%clickhouse.password%'
                query:
                    database: '%clickhouse.database%'

日志

SimPod\ClickHouseClient\Client\Http\LoggerPlugin 可与 HTTPlug PluginClient 一起使用。

这是

<?php

declare(strict_types=1);

namespace Cdn77\Mon\Core\Infrastructure\Symfony\Service\ClickHouse;

use Http\Client\Common\PluginClient;
use SimPod\ClickHouseClient\Client\Http\LoggerPlugin;
use SimPod\ClickHouseClient\Logger\SqlLogger;
use Symfony\Component\HttpClient\HttplugClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;

final class HttpClientFactory
{
    public function __construct(private HttpClientInterface $clickHouseClient, private SqlLogger $sqlLogger)
    {
    }

    public function create() : PluginClient
    {
        return new PluginClient(
            new HttplugClient($this->clickHouseClient),
            [new LoggerPlugin($this->sqlLogger)]
        );
    }
}

时区

ClickHouse没有带时区的日期时间。因此,您需要规范化作为参数传递的日期时间的时区,以确保适当的输入格式。

以下将作为 2020-01-31 01:00:00 插入到ClickHouse中。

new DateTimeImmutable('2020-01-31 01:00:00', new DateTimeZone('Europe/Prague'));

如果您的服务器使用 UTC,则该值对您不正确,实际上您需要插入 2020-01-31 00:00:00

通过将 DateTimeZone 传递到 PsrClickHouseClient 构造函数中,启用时区规范化。

new PsrClickHouseClient(..., new DateTimeZone('UTC'));

PSR工厂是?

该库没有实现自己的HTTP。这已经通过 PSR-7, PSR-17 和 PSR-18 完成。该库遵守它,并允许您插入自己的实现(例如HTTPPlug或Guzzle)。

建议使用 composer require nyholm/psr7 用于PSR-17,并使用 composer require php-http/curl-client 用于Curl PSR-18实现(如上例所示)。

同步API

选择

ClickHouseClient::select()

用于 SELECTSHOW 查询。将 FORMAT 添加到查询中,并以所选输出格式返回响应

<?php

use SimPod\ClickHouseClient\Client\ClickHouseClient;
use SimPod\ClickHouseClient\Format\JsonEachRow;
use SimPod\ClickHouseClient\Output;

/** @var ClickHouseClient $client */
/** @var Output\JsonEachRow $output */
$output = $client->select(
    'SELECT * FROM table',
    new JsonEachRow(),
    ['force_primary_key' => 1]
);

选择带参数

ClickHouseClient::selectWithParams()

ClickHouseClient::select() 相同,但还允许 参数绑定

<?php

use SimPod\ClickHouseClient\Client\ClickHouseClient;
use SimPod\ClickHouseClient\Format\JsonEachRow;
use SimPod\ClickHouseClient\Output;

/** @var ClickHouseClient $client */
/** @var Output\JsonEachRow $output */
$output = $client->selectWithParams(
    'SELECT * FROM :table',
    ['table' => 'table_name'],
    new JsonEachRow(),
    ['force_primary_key' => 1]
);

插入

ClickHouseClient::insert()

<?php

use SimPod\ClickHouseClient\Client\ClickHouseClient;

/** @var ClickHouseClient $client */
$client->insert('table', $data, $columnNames);

如果提供了 $columnNames 并且是键-值数组,则根据它生成列名,并将值作为参数传递

$client->insert('table', [[1,2]], ['a' => 'Int8', 'b' => 'String']); 生成 INSERT INTO table (a,b) VALUES ({p1:Int8},{p2:String}) 并将值传递给查询。

如果提供了 $columnNames,则根据它生成列名。

$client->insert('table', [[1,2]], ['a', 'b']); 生成 INSERT INTO table (a,b) VALUES (1,2)

如果省略了 $columnNames,则从 $data 读取列名。

$client->insert('table', [['a' => 1,'b' => 2]]); 生成 INSERT INTO table (a,b) VALUES (1,2)

列名只从第一个项目读取。

$client->insert('table', [['a' => 1,'b' => 2], ['c' => 3,'d' => 4]]); 生成 INSERT INTO table (a,b) VALUES (1,2),(3,4)

如果没有提供,它们也不会传递。

$client->insert('table', [[1,2]]); 生成 INSERT INTO table VALUES (1,2)

异步API

选择

参数 "绑定"。

<?php

use SimPod\ClickHouseClient\Sql\SqlFactory;
use SimPod\ClickHouseClient\Sql\ValueFormatter;

$sqlFactory = new SqlFactory(new ValueFormatter());

$sql = $sqlFactory->createWithParameters(
    'SELECT :param',
    ['param' => 'value']
);

这会产生 SELECT 'value' 并可以传递给 ClickHouseClient::select()

支持的类型有

  • 标量
  • DateTimeImmutable(《\DateTime》不受支持,因为《ValueFormatter》可能会修改它的时区,所以它不被认为是安全的)
  • 表达式
  • 实现 __toString() 的对象

原生查询参数

提示

官方文档

<?php

use SimPod\ClickHouseClient\Client\PsrClickHouseClient;

$client = new PsrClickHouseClient(...);

$output = $client->selectWithParams(
    'SELECT {p1:String}',
    ['param' => 'value']
);

所有类型都受支持(设计上除外 AggregateFunctionSimpleAggregateFunctionNothing)。您还可以将 DateTimeInterface 传递到 Date* 类型或将本地数组传递到 ArrayTupleNativeGeo 类型

表达式

为了表示复杂表达式,存在 SimPod\ClickHouseClient\Sql\Expression 类。当传递给 SqlFactory 时,其值将被评估。

要将例如 UUIDStringToNum('6d38d288-5b13-4714-b6e4-faa59ffd49d8') 传递到 SQL

<?php

use SimPod\ClickHouseClient\Sql\Expression;

Expression::new("UUIDStringToNum('6d38d288-5b13-4714-b6e4-faa59ffd49d8')");
<?php

use SimPod\ClickHouseClient\Sql\ExpressionFactory;
use SimPod\ClickHouseClient\Sql\ValueFormatter;

$expressionFactory = new ExpressionFactory(new ValueFormatter());

$expression = $expressionFactory->templateAndValues(
    'UUIDStringToNum(%s)',
    '6d38d288-5b13-4714-b6e4-faa59ffd49d8'
);

代码片段

有一些方便的查询,例如获取数据库大小、表列表、当前数据库等。

为了防止客户端 API 污染,这些被提取到代码片段中。

获取当前数据库名的示例

<?php

use SimPod\ClickHouseClient\Snippet\CurrentDatabase;

$currentDatabaseName = CurrentDatabase::run($client);

列表

  • 当前数据库
  • 数据库大小
  • 部分
  • 显示创建表
  • 显示数据库
  • 表大小
  • 版本