QuestDB is a high-performance open-source time-series database designed for fast ingestion and low-latency SQL queries. It supports multiple ingestion protocols including PostgreSQL wire protocol for queries and InfluxDB Line Protocol (ILP) for high-speed time-series data ingestion.
Add the following dependency to your project file:
NuGet
1
dotnetaddpackageTestcontainers.QuestDb
You can start a QuestDB container instance from any .NET application. This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.
To execute the tests, use the command dotnet test from a terminal.
Tip
For the complete source code of this example and additional information, please refer to our test projects.
Connection Methods
PostgreSQL Wire Protocol (SQL Queries)
QuestDB supports the PostgreSQL wire protocol for querying data:
12345678
varconnectionString=questDbContainer.GetConnectionString();// Returns: "Host=localhost;Port=xxxxx;Database=qdb;Username=admin;Password=quest"usingvarconnection=newNpgsqlConnection(connectionString);connection.Open();usingvarcommand=newNpgsqlCommand("SELECT * FROM sensors ORDER BY ts DESC LIMIT 10;",connection);usingvarreader=command.ExecuteReader();
InfluxDB Line Protocol (High-Speed Ingestion)
For high-performance time-series data ingestion, use ILP over TCP:
varrestApiAddress=questDbContainer.GetRestApiAddress();usingvarhttpClient=newHttpClient();httpClient.BaseAddress=newUri(restApiAddress);// Execute SQL via RESTvarresponse=awaithttpClient.GetAsync("/exec?query=SELECT * FROM sensors");
Web Console
Access the interactive Web Console:
12
varwebConsoleUrl=questDbContainer.GetWebConsoleUrl();// Open in browser: http://localhost:xxxxx
// 1. Create table via SQLawaitusingvarconnection=newNpgsqlConnection(questDbContainer.GetConnectionString());awaitconnection.OpenAsync();awaitusingvarcreateCommand=newNpgsqlCommand("CREATE TABLE IF NOT EXISTS sensors (ts TIMESTAMP, device_id SYMBOL, temperature DOUBLE, humidity DOUBLE) timestamp(ts) PARTITION BY DAY;",connection);awaitcreateCommand.ExecuteNonQueryAsync();// 2. Ingest data via ILP (high-speed)usingvartcpClient=newTcpClient();awaittcpClient.ConnectAsync(questDbContainer.GetInfluxLineProtocolHost(),questDbContainer.GetInfluxLineProtocolPort());usingvarstream=tcpClient.GetStream();usingvarwriter=newStreamWriter(stream){AutoFlush=true};for(inti=0;i<10000;i++){awaitwriter.WriteLineAsync($"sensors,device_id=dev{i % 10} temperature={20 + i % 30},humidity={50 + i % 40}");}// 3. Query results via SQLawaitusingvarqueryCommand=newNpgsqlCommand(@"SELECT device_id, AVG(temperature) as avg_temp, AVG(humidity) as avg_humidity FROM sensors WHERE ts > dateadd('h', -1, now()) GROUP BY device_id;",connection);awaitusingvarreader=awaitqueryCommand.ExecuteReaderAsync();while(awaitreader.ReadAsync()){vardeviceId=reader.GetString(0);varavgTemp=reader.GetDouble(1);varavgHumidity=reader.GetDouble(2);Console.WriteLine($"{deviceId}: {avgTemp}°C, {avgHumidity}%");}
Time-Series Features
QuestDB extends SQL with powerful time-series operators: