<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[Tiger Data Blog]]></title>
        <description><![CDATA[Insights, product updates, and tips from TigerData (Creators of TimescaleDB) engineers on Postgres, time series & AI. IoT, crypto, and analytics tutorials & use cases.]]></description>
        <link>https://www.tigerdata.com/blog</link>
        <image>
            <url>https://www.tigerdata.com/icon.ico</url>
            <title>Tiger Data Blog</title>
            <link>https://www.tigerdata.com/blog</link>
        </image>
        <generator>RSS for Node</generator>
        <lastBuildDate>Tue, 07 Apr 2026 10:03:01 GMT</lastBuildDate>
        <atom:link href="https://www.tigerdata.com/blog" rel="self" type="application/rss+xml"/>
        <ttl>60</ttl>
        <item>
            <title><![CDATA[Slow Grafana Performance? Learn How to Fix It Using Downsampling]]></title>
            <description><![CDATA[Learn about two common visualization problems in Grafana—slow dashboards and noisy data—and how to fix them using downsampling in TimescaleDB.]]></description>
            <link>https://www.tigerdata.com/blog/slow-grafana-performance-learn-how-to-fix-it-using-downsampling</link>
            <guid isPermaLink="true">https://www.tigerdata.com/blog/slow-grafana-performance-learn-how-to-fix-it-using-downsampling</guid>
            <category><![CDATA[Data Visualization]]></category>
            <category><![CDATA[PostgreSQL]]></category>
            <category><![CDATA[PostgreSQL Tips]]></category>
            <dc:creator><![CDATA[Brian Rowe]]></dc:creator>
            <pubDate>Thu, 23 Jun 2022 13:03:01 GMT</pubDate>
            <media:content medium="image" href="https://timescale.ghost.io/blog/content/images/2022/06/Grafana-Downsampling-post--1-.png">
            </media:content>
            <content:encoded><![CDATA[<h2 id="downsampling-in-grafana">Downsampling in Grafana</h2><p>Graphs are awesome. They allow us to understand data quicker and easier, highlighting trends that otherwise wouldn’t stand out. And Grafana, the open-source visualization tool, is a fantastic tool for creating graphs, especially for time-series data. </p><p>If you have some data that you want to analyze visually, you just hook it up to your Grafana instance, set up your query, and you’re off to the races. (If you’re new to Grafana and Timescale, don’t worry, we’ve got you covered. See our Getting Started with Grafana and TimescaleDB <a href="https://docs.timescale.com/timescaledb/latest/tutorials/grafana/">docs</a> or <a href="https://youtube.com/playlist?list=PLsceB9ac9MHTjwvV18QJnPcLrTXm_Q-Ft">videos</a> to get up and running).</p><p>However, while Grafana is an awesome tool for generating graphs, problems still arise when we have too much data. Extremely large datasets can be prohibitively slow to load, leading to frustrated users or, worse, unusable dashboards.</p><p>These large <a href="https://timescale.ghost.io/blog/what-the-heck-is-time-series-data-and-why-do-i-need-a-time-series-database-dcf3b1b18563/">time-series datasets</a> are especially common in industries like financial services, the Internet of Things, and <a href="https://timescale.ghost.io/blog/observability-powered-by-sql-understand-your-systems-like-never-before-with-opentelemetry-traces-and-postgresql/">observability</a> as data can be relentless, often generated at high rates and volumes.</p><p>To better understand the problems that can occur when we have extremely large datasets, consider the example of stock ticker data and this graph showing 30 days' worth of trades for five different stocks (AAPL, TSLA, NVDA, MSFT, and AMD):</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/06/Grafana-Downsampling-2--1-.png" class="kg-image" alt="" loading="lazy" width="512" height="257"></figure><p>This graph is composed of five queries which collectively contain nearly 1.3 million data points and takes nearly 20 seconds to load, pan, or zoom!</p><p>Even with more manageable amounts of data, our graphs can still sometimes be difficult to interpret if the data is too noisy. If the daily variance of our data is so high, it can hide the underlying trends that we're looking for. Consider this graph showing the volume of taxi trips taken in New York City over a two-month period:</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/06/Grafana-downsampling-3--1-.png" class="kg-image" alt="" loading="lazy" width="512" height="225"></figure><p>That spike a third of the way in may be a significant shift in volume, and those lower peaks toward the right edge might be a significant decline. It's not immediately obvious though, and certainly, this is not the powerful tool we want our graphs to be.</p><p>We can use different types of downsampling to solve the problems of slow-loading Grafana dashboards and noisy graphs, respectively. Downsampling is the practice of replacing a large set of data points with a smaller set.</p><p>We’ll implement our solutions using two of <a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/hyperfunctions/">TimescaleDB’s hyperfunctions</a> for downsampling, making it easy to manipulate and analyze time-series data with fewer lines of SQL code. We’ll look at one hyperfunction for downsampling using the Largest Triangle Three Buckets or <code>lttb()</code> method, and another for downsampling using the ASAP smoothing algorithm, both of which come pre-installed with Timescale or can be accessed via the <a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/hyperfunctions/install-toolkit/">timescaledb_toolkit extension</a> if you self-manage your database.</p><h2 id="example-1-load-faster-dashboards-with-lttb-downsampling">Example 1: Load faster dashboards with lttb( ) downsampling</h2><p>In our first example, which plots the prices for five stocks over a 30-day period, the problem is that we have way too much data, resulting in a slow-loading graph. This is because the <a href="https://docs.timescale.com/getting-started/latest/add-data/#about-the-dataset">real-time stocks dataset</a> we’re using has upwards of 10,000 points per day for each stock symbol!</p><p>Given the timeframe of our analysis (30 days), this is far more data than we need to spot a trend, and the time needed to load this graph is dominated by the cost of fetching all of the data.</p><p>To solve this problem, we need to find a way to reduce the number of data points we're getting from our data source. Unfortunately, doing this in a manner that doesn't drastically deform our graph is actually a very tricky problem. For example, let’s look at just the NVDA ticker price:</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/06/Grafana-Downsampling-4--1-.png" class="kg-image" alt="" loading="lazy" width="512" height="224"></figure><p><br>Here's what we see if we just naively take the 10-minute average for the NVDA symbol (overlaid in yellow on the original data).</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/06/Grafana-Downsampling-5--1-.png" class="kg-image" alt="" loading="lazy" width="512" height="224"></figure><p>The graph of the average (mean) roughly follows the underlying data but completely smooths away almost all of the peaks and valleys, and those are the most interesting parts of the dataset! Taking the first or last point from each bucket results in an even more skewed graph, as the outlying points have no weight unless they happen to fall in just the right spot.  </p><p>What we need is a way to capture the most interesting point from each bucket. To do that, we can use the <a href="https://docs.timescale.com/api/latest/hyperfunctions/downsample/lttb/"><code>lttb()</code> algorithm</a> which gives us a downsampled graph that follows the pattern of the original graph quite closely. (As an aside, <a href="http://skemman.is/stream/get/1946/15343/37285/3/SS_MSthesis.pdf"><code>lttb(</code></a><code>)</code> was invented by <a href="https://is.linkedin.com/in/sveinn-steinarsson">Sveinn Steinarsson</a> in his master’s thesis).</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/06/Grafana-Downsampling-6--1-.png" class="kg-image" alt="" loading="lazy" width="512" height="224"></figure><p>Using <a href="http://skemman.is/stream/get/1946/15343/37285/3/SS_MSthesis.pdf"><code>lttb(</code></a><code>)</code>, the downsampled data is barely distinguishable from the original, <strong>despite having less than 0.5 % of the points!</strong></p><p><a href="http://skemman.is/stream/get/1946/15343/37285/3/SS_MSthesis.pdf"><code>lttb(</code></a><code>)</code> works by keeping the same first and last point as the original data but dividing the rest of the data into equal intervals. For each interval, it then tries to find the most impactful point. It does this by building a triangle for each point in the interval with the point selected from the previous interval and the average of the points in the next interval. These triangles are compared with one another by area. The largest resulting triangle corresponds to the point in the interval that has the largest impact on how the graph looks.</p><p>As we see above, the result is a graph that very closely resembles the original graph. What's not as obvious is that the raw data was nearly 315,000 rows of data that took over five seconds to pull into our dashboard.  The <a href="http://skemman.is/stream/get/1946/15343/37285/3/SS_MSthesis.pdf"><code>lttb(</code></a><code>)</code> data was 1,404 rows that took less than one second to fetch.  </p><p>Here is the SQL query we used in our Grafana panel to get the <a href="http://skemman.is/stream/get/1946/15343/37285/3/SS_MSthesis.pdf"><code>lttb(</code></a><code>)</code> data.</p><pre><code class="language-SQL">SELECT
  time AS "time",
  value AS "NVDA lttb"
FROM unnest((
    SELECT lttb(time, price, 2 * (($__to - $__from) / $__interval_ms)::int)
    FROM stocks_real_time
    WHERE symbol = 'NVDA' AND $__timeFilter("time"))
)
  ORDER BY 1;
</code></pre>
<p>As you can see, the real work here is done by the <a href="https://docs.timescale.com/api/latest/hyperfunctions/downsample/lttb/"><code>lttb()</code></a><a href="https://docs.timescale.com/api/latest/hyperfunctions/downsample/lttb/"> hyperfunction</a> call in the inner <code>SELECT</code>.  This function takes the <code>time</code> and <code>value</code> columns from our table, and also a third integer specifying the target resolution, which is the number of points it should return.  </p><p>Unfortunately, Grafana doesn't directly expose the panel width in pixels to us, but we can get an approximation from the <a href="https://grafana.com/docs/grafana/latest/variables/variable-types/global-variables/#__interval"><code>$__interval</code> global variable</a> (which is approximately <code>(to - from) / resolution)</code>. For this graph, the interval was a bit of an underestimation, hence us doubling it in the function above.</p><p>Our <code>lttb()</code> hyperfunction <a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/hyperfunctions/function-pipelines/#timevectors">returns a custom </a><a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/hyperfunctions/function-pipelines/#timevectors"><code>timevector</code></a><a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/hyperfunctions/function-pipelines/#timevectors"> object</a>, which uses <code>unnest</code> to get <code>time</code>, <code>value</code> rows that Grafana can understand and plot.</p><h2 id="example-2-find-signal-from-noisy-datasets-with-asap-smoothing-downsampling">Example 2: Find signal from noisy datasets with ASAP smoothing downsampling</h2><p><code>lttb()</code> is a fantastic downsampling algorithm for giving us a subset of points that maintain the visual appearance of a graph. However, sometimes the problem is that the original graph is so noisy that the long-term trends we're trying to see are lost in the normal periodic variance of the data. This is the case we saw in our second example above, that of taxi data (and shown below):</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/06/Graf-Downsampling-7--1-.png" class="kg-image" alt="" loading="lazy" width="512" height="225"></figure><p>In this case, what we're interested in isn't a way of just reducing the number of points in a graph (as we saw before, that ends up with a graph that looks the same!), but doing so in a manner that smooths away the noise.</p><p>We can use a downsampling technique called<a href="https://arxiv.org/pdf/1703.00983.pdf"> Automated Smoothing for Attention Prioritization (ASAP)</a>, which was developed by <a href="https://twitter.com/kexinrong?lang=en">Kexin Rong</a> and <a href="https://www.linkedin.com/in/pbailis">Peter Bailis</a>.</p><p><a href="https://docs.timescale.com/api/latest/hyperfunctions/downsample/asap/">ASAP works by analyzing the data for intervals of high autocorrelation.</a> Think of this as finding the size of the repeating shape of a graph, so maybe 24 hours for our taxi data, or even 168 hours (one week). Once ASAP has found the range with the highest autocorrelation, it will smooth out the data by computing a rolling average using that range as the window size.</p><p>For instance, if you have perfectly regular data, ASAP should mostly smooth everything away to the underlying flat trend, as in the following example: <br></p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/06/Graf-Downsa-8--1-.png" class="kg-image" alt="" loading="lazy" width="512" height="254"></figure><p>The green line here is the raw data. It is generated as a sine wave with an interval of 20 and an offset of 100 that repeats daily. The yellow line is the ASAP algorithm applied to the data, showing that the graph is entirely regular noise with no interesting underlying fluctuation.</p><p>Obviously ASAP can work well on this type of synthetic data, but let's see how it does with our taxi data.</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/06/Graf-Downs-9--1-.png" class="kg-image" alt="" loading="lazy" width="512" height="254"></figure><p>Here it becomes very obvious that there was a significant dip over from about 11/26 to 12/03, which happens to be Thanksgiving weekend, a US public holiday weekend that occurs at the end of November every year. We can see this even more dramatically by selecting only the ASAP output and letting Grafana auto-adjust the scale:</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/06/Grafana-Downsampling-10--1-.png" class="kg-image" alt="" loading="lazy" width="512" height="252"></figure><p><br>The data for this graph is the<a href="http://www.futuredata.io.s3-website-us-west-2.amazonaws.com/asap/"> taxi trips CSV file</a>. The SQL query we're running in Grafana is this:</p><pre><code class="language-SQL">SELECT
  time AS "time",
  value AS "asap"
FROM unnest((
  SELECT asap_smooth(time, value, (($__to - $__from) / '$__interval_ms')::integer)
  FROM taxidata
  WHERE $__timeFilter("time"))
)
ORDER BY 1
</code></pre>
<p>As in example 1 above, the <code>asap_smooth</code> hyperfunction does most of the work here, taking the time and value columns, as well as a target resolution as arguments. We use the same trick from example 1 to approximate the panel width from Grafana's global variables.</p><h2 id="learn-more">Learn More</h2><p>Eager to try downsampling or learn more about other hyperfunctions? Check out our <a href="https://docs.timescale.com/api/latest/hyperfunctions/downsample/#downsample">downsample</a> and <a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/hyperfunctions/#learn-hyperfunction-basics-and-install-timescale-toolkit">hyperfunctions</a> docs for more information on how hyperfunctions can help you efficiently query and analyze your data. </p><p>Looking for more Grafana guides? Here are our <a href="https://docs.timescale.com/timescaledb/latest/tutorials/grafana/">Grafana tutorials</a> and our <a href="https://timescale.ghost.io/blog/grafana-webinar-1-recap/">Grafana 101 Creating Awesome Visualizations</a> for more support on visualizations in Grafana. </p><p>If you need a database to store your time-series data and power your dashboards, try <a href="https://console.cloud.timescale.com/signup">Timescale</a>, our fast, easy-to-use, and reliable cloud-native data platform for <a href="https://www.tigerdata.com/blog/time-series-introduction" rel="noreferrer">time series</a> built on PostgreSQL. (You can sign up for a 30-day free trial, no credit card required.)</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing Hyperfunctions: New SQL Functions to Simplify Working With Time-Series Data in PostgreSQL]]></title>
            <description><![CDATA[TimescaleDB hyperfunctions are pre-built functions for the most common and difficult queries that developers write today in TimescaleDB and PostgreSQL. Hyperfunctions help developers measure what matters in time-series data, which generates massive, ever-growing streams of information.]]></description>
            <link>https://www.tigerdata.com/blog/introducing-hyperfunctions-new-sql-functions-to-simplify-working-with-time-series-data-in-postgresql</link>
            <guid isPermaLink="true">https://www.tigerdata.com/blog/introducing-hyperfunctions-new-sql-functions-to-simplify-working-with-time-series-data-in-postgresql</guid>
            <category><![CDATA[Announcements & Releases]]></category>
            <category><![CDATA[Engineering]]></category>
            <category><![CDATA[PostgreSQL]]></category>
            <dc:creator><![CDATA[Joshua Lockerman]]></dc:creator>
            <pubDate>Tue, 13 Jul 2021 13:02:11 GMT</pubDate>
            <media:content medium="image" href="https://timescale.ghost.io/blog/content/images/2021/07/ryan-stone-OlxJVn9fxz4-unsplash.jpg">
            </media:content>
            <content:encoded><![CDATA[<p></p><p>Today, we’re excited to launch <strong>TimescaleDB hyperfunctions</strong>, a series of SQL functions within TimescaleDB that make it easier to manipulate and analyze time-series data in PostgreSQL with fewer lines of code. You can use hyperfunctions to calculate percentile approximations of data, compute time-weighted averages, downsample and smooth data, and perform faster <code>COUNT DISTINCT</code> queries using approximations. Moreover, hyperfunctions are “easy” to use: you call a hyperfunction using the same SQL syntax you know and love.</p>
<p>At Timescale, our mission is to <a href="https://www.timescale.com/products" rel="noreferrer">enable every software developer to store, analyze, and build on top of their time-series data</a> so that they can measure what matters in their world: IoT devices, IT systems, marketing analytics, user behavior, financial metrics, and more.</p><p>We made the decision early in the design of TimescaleDB to build on top of PostgreSQL. We believed then, as we do now, that building on the <a href="https://db-engines.com/en/blog_post/85">world’s fastest-growing database</a> would have numerous benefits for our customers. Perhaps the biggest of these advantages is in developer productivity. Developers can use the tools and frameworks they know and love and bring all their skills and expertise with SQL with them.</p><p>SQL is a powerful language and we believe that by adding a specialized set of functions for time-series analysis, we can make it even better.</p><p>Today, there are nearly three million active TimescaleDB databases running mission-critical time-series workloads across industries. Time-series data comes at you fast, sometimes generating millions of data points per second. In order to measure everything that matters, you need to capture all of the data you possibly can. Because of the volume and rate of information, time-series data can be complex to query and analyze. </p><p>As we interviewed customers and learned how they analyze and manipulate time-series data, we noticed several common queries begin to take shape. Often, these queries were difficult to compose in standard SQL. TimescaleDB hyperfunctions are a series of SQL functions to address the most common, and often most difficult, queries developers write today. We made the decision to take the hard path ourselves so that we could give developers an easier path.</p><h3 id="hyperfunctions-included-in-this-initial-release">Hyperfunctions included in this initial release</h3><p>Today, we’re releasing several hyperfunctions, including:</p><ul>
<li><strong>Time-Weighted Average</strong> allows you to take the average over an irregularly spaced dataset that only includes changepoints.</li>
<li><strong>Percentile-Approximation</strong> brings percentile analysis to more workflows. When used with <a href="https://timescale.ghost.io/blog/blog/continuous-aggregates-faster-queries-with-automatically-maintained-materialized-views/">continuous aggregates</a>, you can compute percentiles over any time range of your dataset in near real-time and use them for baselining and normalizing incoming data. For maximum control, we provide implementations of two different approximation algorithms:
<ul>
<li><strong>Uddsketch</strong> gives formal guarantees to the accuracy of approximate percentiles, in exchange for always returning a range of possible values.</li>
<li><strong>T-Digest</strong> gives fuzzier guarantees which allow it to be more precise at the extremes of the distribution.</li>
</ul>
</li>
<li><strong>Hyperloglog</strong> enables faster approximate <code>COUNT DISTINCT</code>, making it easier to track how the cardinality of your data changes over time.</li>
<li><strong>Counter Aggregate</strong> enables working with counters in an ergonomic SQL-native manner.</li>
<li><strong>ASAP Smoothing</strong> smooths datasets to bring out the most important features when graphed.</li>
<li><strong>Largest Triangle Three Buckets Downsampling</strong> reduces the number of elements in a dataset while retaining important features when graphed.</li>
<li><strong>Stats-agg</strong> makes using rolling, cumulative and normalized statistics as easy as their standard counterparts.</li>
</ul>
<p>Note that Hyperfunctions work on TimescaleDB <a href="https://docs.timescale.com/api/latest/hypertable/">hypertables</a>, as well as regular PostgreSQL tables.</p><h3 id="new-sql-functions-not-new-sql-syntax">New SQL functions, not new SQL syntax</h3><p>We made the decision to create <strong>new SQL functions</strong> for each of the time-series analysis and manipulation capabilities above. This stands in contrast to other efforts which aim to improve the developer experience by introducing new SQL <em>syntax</em>. </p><p>While introducing new syntax with new keywords and new constructs may have been easier from an implementation perspective, we made the deliberate decision not to do so since we believe that it actually leads to a worse experience for the end-user. </p><p>New SQL syntax means that existing drivers, libraries, and tools may no longer work. This can leave developers with more problems than solutions as their favorite tools, libraries, or drivers may not support the new syntax, or may require time-consuming modifications to do so. </p><p>On the other hand, new SQL functions mean that your query will run in every visualization tool, database admin tool, or data analysis tool. We have the freedom to create custom functions, aggregates, and procedures that help developers better understand and work with their data, <strong>and </strong>ensure all their drivers and interfaces still work as expected.</p><h3 id="hyperfunctions-are-written-in-rust">Hyperfunctions are written in Rust</h3><p>Rust was our language of choice for developing the new hyperfunctions. We chose it for its superior productivity, community, and the <a href="https://github.com/zombodb/pgx">pgx software development kit</a>. We felt Rust was a more friendly language for a project like ours and would encourage more community contributions. </p><p>The inherent safety of Rust means we could focus more time on feature development rather than worrying about how the code is written. The extensive Rust community (💗 <a href="http://crates.io">crates.io</a>), along with excellent package-management tools, means we can use off-the-shelf solutions for common problems, leaving us more time to focus on the uncommon ones. </p><p>On the topic of community, we found the Rust community to be one of the friendliest on the internet, and its commitment to open source, open communication, and good documentation make it an utter joy to work with. Libraries such as <a href="https://serde.rs/">Serde</a> and <a href="https://github.com/BurntSushi/quickcheck">quickcheck </a>make common tasks a breeze and lets us focus on the code that’s novel to our project, instead of writing boilerplate that's already been written by thousands of others. </p><p>We’d like to shout out ZomboDB’s <a href="https://github.com/zombodb/pgx">pgx</a>, an SDK for building <a href="https://www.tigerdata.com/blog/top-8-postgresql-extensions" rel="noreferrer">Postgres extensions</a> using Rust. Pgx provides tools to generate extension scripts from Rust files and bind Rust functions to Postgres functions, as well as tools to set up, run, and test PostgreSQL instances. (For us, it’s been an amazing tool and experience with incredible benefits – we estimate that pgx has reduced our workload by at least one-third!.)</p><h3 id="next-steps">Next steps</h3><p>In the rest of this post, we detail why we chose to build new SQL functions (not new SQL syntax), and explore each hyperfunction and its example usage.</p><p>But<strong> if you’d like to get started with hyperfunctions right away, the easiest way to do so is with a fully-managed TimescaleDB service</strong>. <a href="https://console.cloud.timescale.com/signup">Try it for free</a> (no credit card required) for 30 days. Hyperfunctions are pre-loaded on each new database service on Timescale, so after you’ve created a new service, you’re all set to use them!</p><p><strong>If you prefer to manage your own database instances, you can </strong><a href="https://github.com/timescale/timescaledb-toolkit"><strong>download and install the timescaledb_toolkit extension</strong></a> on GitHub for free, after which you’ll be able to use all the hyperfunctions listed above. </p><p>Finally, we love building in public. You can view our <a href="https://github.com/timescale/timescaledb-toolkit">upcoming roadmap on GitHub</a> for a list of proposed features, as well as features we’re currently implementing and those that are available to use today. </p><p>We also welcome feedback from the community (it helps us prioritize the features users really want). To contribute feedback, comment on an <a href="https://github.com/timescale/timescaledb-toolkit/issues">open issue</a> or in a <a href="https://github.com/timescale/timescaledb-toolkit/discussions">discussion thread</a> in GitHub.</p><p>To learn more about hyperfunctions, please continue reading.</p><h2 id="building-new-sql-functions-instead-of-reinventing-syntax">Building new SQL functions instead of reinventing syntax</h2><p>SQL is the <a href="https://insights.stackoverflow.com/survey/2020#most-popular-technologies">third most popular programming language in the world</a>. It’s the language known and loved by many software developers, data scientists, and business analysts the world over, and it's a big reason we chose to build TimescaleDB on top of PostgreSQL in the first place.</p><p>Similarly, we choose to make our APIs user-friendly without breaking full SQL compatibility. This means we can create custom functions, aggregates, and procedures but no new syntax - and all the drivers and interfaces can still work. You get the peace of mind that your query will run in every visualization tool, database admin tool, or data analysis tool that speaks SQL.</p><p>SQL is powerful and it’s even <a href="http://blog.coelho.net/database/2013/08/17/turing-sql-1.html">Turing complete</a>, so you can technically do anything with it. But that doesn’t mean you’d want to 😉. Our hyperfunctions are made to make complex analysis and time-series manipulation in SQL simpler, without undermining the guarantees of full SQL compatibility. We’ve spent a large amount of our time on design; prototyping and just writing out different names and calling conventions for clarity and ease of use. </p><p>Our guiding philosophy is to make simple things easy and complex things possible. We enable things that <em>feel</em> like they should be straightforward, like using a single function call to calculate a time-weighted average of a single item over a time period. We also enable operations that would otherwise be prohibitively expensive (in terms of complexity to write) or would previously take too long to respond to be useful. For example, calculating a rolling time-weighted average of each item normalized to the monthly average of the whole group of things.</p><p>For example, we’ve implemented a default for percentile approximation called <code>percentile_agg</code>  that should work for most users, while also exposing the lower level <code>UDDsketch</code> and <code>tdigest</code> implementations for users who want to have more control and get into the weeds.</p>
<p>Another advantage of using SQL functions rather than new syntax is that we bring your code closer to your data, rather than forcing you to take your data to your code. Simply put, you can now perform more sophisticated analysis and manipulation operations on your data right inside your database, rather than creating data pipelines to funnel data into Python or other analysis libraries to conduct analysis there. </p><p>We want to make the more complex analysis simpler and easier in the database not just because we want to build a good product, but also because it’s far, far more efficient to do your analysis as close to the data as possible, and then get aggregated or other simpler results that get passed back to the user. </p><p>This is because the network transmission step is often the slowest and most expensive part of many calculations, and because the serialization and deserialization overhead can be very large as you get to large datasets. So by making these functions and all sorts of analysis simpler to perform in the database, nearer to the data, developers save time and money.</p><p>Moreover, while you could perform some of the complex analysis enabled by hyperfunctions in other languages inside the database (e.g., programs in Python or R), hyperfunctions now enable you to perform such sophisticated time-series analysis and manipulation in SQL right in your query statements, making them more accessible.</p><h2 id="hyperfunctions-released-today">Hyperfunctions released today</h2><p>Hyperfunctions refactor some of the most gnarly SQL queries for time-series data into concise, elegant functions that feel natural to any developer that knows SQL. Let’s walk through the hyperfunctions we’re releasing today and the ones that will be available soon.</p><p>Back in January, <a href="https://timescale.ghost.io/blog/blog/time-series-analytics-for-postgresql-introducing-the-timescale-analytics-project/">when we launched our initial hyperfunctions release</a>, we asked for feedback and input from the community. We want this to be a community-driven project, so for our 1.0 release, we’ve prioritized several features requested by community members. We’ll have a brief overview here, with a technical deep dive into each family of functions in a series of separate blog posts in the coming weeks.</p><p><strong>Time-weighted averages</strong></p><p>Time-series averages can be complicated to calculate; generally, you need to determine how long each value has been recorded in order to know how much to weigh them. While doing this in native SQL is <em>possible</em>, it is extremely error-prone and unwieldy. More damningly, the SQL needed would not work in every context. In particular, it would not work in TimescaleDB’s automatically refreshing materialized views, <a href="https://timescale.ghost.io/blog/blog/continuous-aggregates-faster-queries-with-automatically-maintained-materialized-views/">continuous aggregates</a>, so users who wanted to calculate time-weighted averages over multiple time intervals would be forced to rescan the entire dataset for each average so calculated. Our time-weighted average hyperfunction removes this complexity and can be used in continuous aggregates to make multi-interval time-weighted averages as cheap as summing a few sub-averages.</p><p>Here’s an example of using time-weighted averages for an IoT use case, specifically to find the average temperature in a set of freezers over time. (Notice how it takes sixteen lines of complex code to find the time-weighted average using regular SQL, compared just five lines of code with <code>SELECT</code> statements when using the TimescaleDB hyperfunction):</p>
<p><strong>Time-weighted average using TimescaleDB hyperfunction</strong></p><pre><code class="language-SQL">SELECT freezer_id, 
	avg(temperature), 
	average(time_weight('Linear', ts, temperature)) as time_weighted_average 
FROM freezer_temps
GROUP BY freezer_id;
</code></pre>
<pre><code class="language-output"> freezer_id |  avg  | time_weighted_average 
------------+-------+-----------------------
          1 | 10.35 |     6.802777777777778
</code></pre>
<p><strong>Time-weighted average using regular SQL</strong></p><pre><code class="language-SQL">WITH setup AS (
	SELECT lag(temperature) OVER (PARTITION BY freezer_id ORDER BY ts) as prev_temp, 
		extract('epoch' FROM ts) as ts_e, 
		extract('epoch' FROM lag(ts) OVER (PARTITION BY freezer_id ORDER BY ts)) as prev_ts_e, 
		* 
	FROM  freezer_temps), 
nextstep AS (
	SELECT CASE WHEN prev_temp is NULL THEN NULL 
		ELSE (prev_temp + temperature) / 2 * (ts_e - prev_ts_e) END as weighted_sum, 
		* 
	FROM setup)
SELECT freezer_id, 
	avg(temperature),
	sum(weighted_sum) / (max(ts_e) - min(ts_e)) as time_weighted_average 
FROM nextstep
GROUP BY freezer_id;
</code></pre>
<pre><code class="language-output"> freezer_id |  avg  | time_weighted_average 
------------+-------+-----------------------
          1 | 10.35 |     6.802777777777778
</code></pre>
<p><strong>Percentile approximation (UDDsketch &amp; TDigest)</strong></p><p>Aggregate statistics are useful when you know the underlying distribution of your data, but for other cases, they <a href="https://en.wikipedia.org/wiki/Anscombe%27s_quartet">can be misleading</a>. For cases where they don’t work, and for more exploratory analyses looking at the ground truth, <a href="https://en.wikipedia.org/wiki/Percentile">percentiles</a> are useful. </p><p>As useful as it is, percentile analysis comes with one major downside: it needs to store the entire dataset in memory. This means that such analysis is only feasible for relatively small datasets, and even then can take longer than ideal to calculate. </p><p>The approximate-percentile hyperfunctions we’ve implemented suffer from neither of these problems: they take constant storage, and, when combined with automatically refreshing materialized views, they can produce results nearly instantaneously. This performance improvement opens up opportunities to use percentile analysis for use cases and datasets where it was previously unfeasible.</p><p>Here’s an example of using percentile approximation for a DevOps use case, where we alert on response times that are over the 95th percentile:</p><pre><code class="language-SQL">WITH “95th percentile” as (
    SELECT approx_percentile(0.95, percentile_agg(response_time)) as threshold
    FROM response_times
)
SELECT count(*) 
FROM response_times 
AND response_time &gt; “95th percentile”.threshold;
</code></pre>
<p><a href="https://docs.timescale.com/api/latest/hyperfunctions/">See our hyperfunctions docs</a> to get started today. In the coming weeks, we will be releasing a series of blog posts which detail each of the hyperfunctions released today, in the context of using them to solve a real-world problem. </p><h3 id="hyperfunctions-in-public-preview">Hyperfunctions in public preview</h3><p>In addition to the hyperfunctions released today, we’re making several hyperfunctions available for public preview. These include hyperfunctions for downsampling, smoothing, approximate count-distinct, working with counters, and working with more advanced forms of averaging. All of these are available for trial today through our experimental schema, and, with your feedback, will be made available for production usage soon. </p><p>Here’s a tour through each hyperfunction and why we created them:</p><p><strong>Graph Downsampling &amp; Smoothing</strong></p><p>We have two algorithms implemented to help downsample your data for better, faster graphing:</p><p>The first graphing algorithm for downsampling is <a href="https://github.com/timescale/timescaledb-toolkit/blob/main/docs/lttb.md"><strong>Largest triangle three bucket</strong></a><strong> (LTTB)</strong>. LTTB limits the number of points you need to send to your graphing engine while maintaining visual acuity. This means that you don’t try to plot 200,000 points on a graph that’s only 2000 pixels wide, which is inefficient in terms of network and rendering costs. </p><p>Given an original dataset which looks like the graph below:</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/01/lttb_raw.png" class="kg-image" alt="" loading="lazy" width="716" height="371" srcset="https://timescale.ghost.io/blog/content/images/size/w600/2022/01/lttb_raw.png 600w, https://timescale.ghost.io/blog/content/images/2022/01/lttb_raw.png 716w"></figure><p>We can downsample it to just 34 points with the following query using the LTTB hyperfunction:</p><pre><code>SELECT toolkit_experimental.lttb(time, val, 34)
</code></pre>
<p>The above query yields the following graph, which retains the periodic pattern of the original graph, with just 34 points of data.</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/01/lttb_34.png" class="kg-image" alt="" loading="lazy" width="600" height="371" srcset="https://timescale.ghost.io/blog/content/images/2022/01/lttb_34.png 600w"></figure><p>The second graphing algorithm for downsampling is <a href="https://github.com/timescale/timescaledb-toolkit/blob/main/docs/asap.md"><strong>Automatic smoothing for attention prioritization</strong></a><strong> (ASAP smoothing). </strong>ASAP Smoothing uses optimal moving averages to smooth a graph to remove noise and make sure that trends are obvious to the user, while not over-smoothing and removing all the signals as well. This leads to vastly improved readability. </p><p>For example, the graph below displays 250 years of monthly temperature readings from England (raw data can be found <a href="http://futuredata.stanford.edu/asap/Temp.csv">here</a>):</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/01/ASAP_raw.png" class="kg-image" alt="" loading="lazy" width="809" height="341" srcset="https://timescale.ghost.io/blog/content/images/size/w600/2022/01/ASAP_raw.png 600w, https://timescale.ghost.io/blog/content/images/2022/01/ASAP_raw.png 809w" sizes="(min-width: 720px) 720px"></figure><p>We can run the following query using the ASAP smoothing hyperfunction:</p><pre><code>SELECT toolkit_experimental.asap_smooth(month, value, 800) FROM temperatures
</code></pre>
<p>The result is the graph below, which is much less noisy than the original and one where users can more easily spot trends.</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/01/ASAP_smoothed.png" class="kg-image" alt="Smoothed data" loading="lazy" width="809" height="341" srcset="https://timescale.ghost.io/blog/content/images/size/w600/2022/01/ASAP_smoothed.png 600w, https://timescale.ghost.io/blog/content/images/2022/01/ASAP_smoothed.png 809w" sizes="(min-width: 720px) 720px"></figure><p><a href="https://github.com/timescale/timescaledb-toolkit/blob/main/docs/counter_agg.md"><strong>Counter Aggregates</strong></a></p><p>Metrics generally come in a few different varieties, which many systems have come to call <strong>gauges</strong> and <strong>counters</strong>. A gauge is a typical metric that can vary up or down, something like temperature or percent utilization. A counter is meant to be monotonically increasing. So it keeps track of, say, the total number of visitors to a website. The main difference in processing counters and gauges is that a decrease in the value of a counter (compared to its previous value in the <a href="https://www.tigerdata.com/blog/time-series-introduction" rel="noreferrer">time series</a>) is interpreted as a <strong>reset</strong>.  TimescaleDB’s <a href="https://github.com/timescale/timescaledb-toolkit/blob/main/docs/counter_agg.md">counter aggregate hyperfunctions</a> enable a simple and optimized analysis of these counters. </p><p>For example, despite a dataset being stored like:</p><pre><code>data
------
  10
  20
   0
   5
  15
</code></pre>
<p>We can calculate the delta (along with various other statistics) over this monotonically-increasing counter with the following query using the counter aggregate hyperfunction:</p><pre><code>SELECT toolkit_experimental.delta(
    toolkit_experimental.counter_agg(ts, val))
FROM foo;
</code></pre>
<pre><code> delta
------
  40
</code></pre>
<p><a href="https://github.com/timescale/timescaledb-toolkit/blob/main/docs/hyperloglog.md"><strong>Hyperloglog for Approximate Count Distinct</strong></a></p><p>We’ve implemented a version of the hyperloglog algorithm to do approximate count distinct queries over data in a more efficient and parallelizable fashion. For existing TimescaleDB users, you’d be happy to hear that they work in continuous aggregates, which are automatically refreshing materialized views. </p><p><a href="https://github.com/timescale/timescaledb-toolkit/blob/main/docs/stats_agg.md"><strong>Statistical Aggregates</strong></a></p><p>Calculating rolling averages and other statistical aggregates over tumbling windows is very difficult in standard SQL because to do it accurately you’d need to separate out the different components (i.e., for average, count and sum) and then calculate it yourself. Our statistical aggregates allow you to simply do this, with simple <code>rollup</code>.</p>
<p>To follow the progress and contribute to improving these (and future) hyperfunctions, you can view our <a href="https://github.com/timescale/timescaledb-toolkit">roadmap on GitHub.</a> Our development process is heavily influenced by community feedback, so your comments on <a href="https://github.com/timescale/timescaledb-toolkit/issues">issues</a> and <a href="https://github.com/timescale/timescaledb-toolkit/discussions">discussion threads</a> will help determine which features get prioritized, and when they’re stabilized for release.</p><h2 id="next-steps-1">Next Steps</h2><p><a href="https://console.cloud.timescale.com/signup">Try hyperfunctions today</a> with a fully-managed Timescale service (no credit card required, free for 30 days). Hyperfunctions are pre-loaded on each new database service on Timescale, so after you’ve created a new service, you’re all set to use them!</p><p>If you prefer to manage your own database instances, you can <a href="https://github.com/timescale/timescaledb-toolkit">download and install the timescaledb_toolkit extension</a> on GitHub for free, after which you’ll be able to use all the hyperfunctions listed above. </p><p>We love building in public. You can view our <a href="https://github.com/timescale/timescaledb-toolkit">upcoming roadmap on GitHub</a> for a list of proposed features, as well as features we’re currently implementing and those that are available to use today. We also welcome feedback from the community (it helps us prioritize the features users really want). To contribute feedback, comment on an <a href="https://github.com/timescale/timescaledb-toolkit/issues">open issue</a> or in a <a href="https://github.com/timescale/timescaledb-toolkit/discussions">discussion thread</a> in GitHub.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Time-Series Analytics for PostgreSQL: Introducing the Timescale Analytics Project]]></title>
            <description><![CDATA[We're excited to announce Timescale Analytics, a new project focused on combining all of the capabilities SQL needs to perform time-series analytics into one Postgres extension. Learn about our plans, why we're sharing it now, and ways to contribute your feedback and ideas.  ]]></description>
            <link>https://www.tigerdata.com/blog/time-series-analytics-for-postgresql-introducing-the-timescale-analytics-project</link>
            <guid isPermaLink="true">https://www.tigerdata.com/blog/time-series-analytics-for-postgresql-introducing-the-timescale-analytics-project</guid>
            <category><![CDATA[Announcements & Releases]]></category>
            <category><![CDATA[PostgreSQL]]></category>
            <category><![CDATA[Analytics]]></category>
            <dc:creator><![CDATA[David Kohn]]></dc:creator>
            <pubDate>Thu, 21 Jan 2021 03:16:15 GMT</pubDate>
            <media:content medium="image" href="https://timescale.ghost.io/blog/content/images/2021/01/alexander-andrews-4JdvOwrVzfY-unsplash.jpg">
            </media:content>
            <content:encoded><![CDATA[<p>We're excited to announce Timescale Analytics, a new project focused on combining all of the capabilities SQL needs to perform time-series analytics into one <a href="https://www.tigerdata.com/blog/top-8-postgresql-extensions" rel="noreferrer">Postgres extension</a>. Learn about our plans, why we're announcing them now, and ways to contribute your feedback and ideas.  </p><p>At Timescale, our mission is to enable every software developer to store, analyze, and build on top of their time-series data so that they can measure what matters in their world: IoT devices, IT systems, marketing analytics, user behavior, financial metrics, and more. To this end, we’ve built a <a href="https://www.timescale.com/products" rel="noreferrer">petabyte-scale, time-series database</a>.</p><p>Today, we’re excited to announce the Timescale Analytics project, an initiative to make Postgres the best way to execute critical time-series queries quickly, analyze time-series data, and extract meaningful information. SQL is a powerful language (we're obviously big fans ourselves), and we believe that by adding a specialized set of functions for time-series analysis, we can make it even better.</p><p>The Timescale Analytics project aims to identify, build, and combine all of the functionality SQL needs to perform time-series analysis into a single extension.</p><p><strong>In other words, the Timescale Analytics extension will be a "one-stop shop" for time-series analytics in PostgreSQL, and we're looking for feedback from the community: what analytical functionality would you find most useful?</strong></p><p>We believe that it is important to develop our code in the open and are requiring radical transparency of ourselves: everything about this project, our priorities, intended features, trade-off discussions, and (tentative) roadmap, are available in <a href="https://github.com/timescale/timescale-analytics">our GitHub repository</a>.</p><p>It is our hope that working like this will make it easier for the community to interact with the project and allow us to respond quickly to community needs. </p><p>To this end, we’re announcing the project as early as possible, so we can get community feedback before we become too invested in a single direction. Over the next few weeks, we’ll be gathering thoughts on initial priorities and opening some sample PRs. Soon after that, we plan to create an initial version of the Timescale Analytics extension for you to experiment with.</p><p>Here are some examples of analytics functions we are considering adding: monotonic counters, tools for graphing, statistical sketching, and pipelining.</p><h2 id="monotonic-counters">Monotonic Counters</h2><p>A monotonically increasing counter is a type of metric often used in time-series analysis. Logically, such a counter should only ever increase, but the value is often read from an ephemeral source that can get reset back to zero at any time (due to crashes or other similar phenomena). To analyze data from such a source, you need to account for these resets: whenever the counter appears to decrease, you assume a reset occurred, and thus, you add the value after the reset to the value immediately prior to the reset.</p><p>Assume we have a counter that measures visitors to a website.  If we were running a new marketing campaign focused on driving people to a new page on our site, we could use the change in the counter to measure the success of the campaign. While this kind of analysis can be performed in stock SQL, it quickly becomes unwieldy.</p><p>Using native SQL, such a query would look like:</p><pre><code class="language-SQL`">SELECT sum(counter_reset_val) + last(counter, ts) - first(counter, ts) as counter_delta 
FROM (
    SELECT *,
        CASE WHEN counter - lag(counter) OVER (ORDER BY ts ASC) &lt; 0
            THEN lag(counter) OVER (ORDER BY ts ASC)
            ELSE 0
        END as counter_reset_val
    FROM user_counter
) f;
</code></pre><p></p><p>This is a relatively simple example, and more sophisticated queries are even more complicated.</p><p><a href="https://github.com/timescale/timescale-analytics/issues/4">One of our first proposals for capabilities to include in Timescale Analytics</a> would make this much simpler, allowing us to  write something like:</p><pre><code class="language-SQL">SELECT delta(counter_agg(counter, ts)) as counter_delta FROM user_counter;</code></pre><p></p><p>There are many examples like this: scenarios where it’s <em>possible</em> to solve the problem in stock SQL, but the resulting code is not exactly easy to write, nor pretty to read.</p><p>We believe we can solve that problem and make writing analytical SQL as easy as any other modern language.</p><h2 id="tools-for-graphing">Tools for Graphing</h2><p>When graphing time-series data, you often need to perform operations such as <a href="https://en.wikipedia.org/wiki/Change_detection">change-point analysis</a>, <a href="https://medium.com/@hayley.morrison/sampling-time-series-data-sets-fc16caefff1b">downsampling</a>, or <a href="https://dawn.cs.stanford.edu/2017/08/07/asap/">smoothing</a>. Right now, these are usually generated with a front-end service, such as <a href="https://grafana.com/">Grafana</a>, but this means the graphs you use are heavily tied to the renderer you’re using. </p><p>Moving these functions to the database offers a number of advantages:</p><ul><li>Users can choose their graphing front-end based on how well it does graphing, not on how well it does data analytics</li><li>Queries can remain consistent across all front-end tools and consumers of your data</li><li>Doing all the work in the database involves shipping a much smaller number of data points over the network</li></ul><p>Key to getting this project working is building the output formats that will work for a variety of front-ends and identifying the necessary APIs. If you have thoughts on the matter, please hop on our <a href="https://github.com/timescale/timescale-analytics/discussions/30">discussion threads</a>.</p><p>A fully worked-out pure-SQL example of a downsampling algorithm is too long to include inline here (for example, a worked-through version of largest-triangle-three-buckets can be found in <a href="https://medium.com/@hayley.morrison/sampling-time-series-data-sets-fc16caefff1b">this blog post</a>) – but with aggregate support could be as simple as:</p><pre><code class="language-SQL">SELECT lttb(time, value, num_buckets=&gt;500) FROM data;</code></pre><p></p><p>This could return a <code>timeseries</code> data type, which could be ingested directly into a tool like Grafana or another language, or it could be unnested to get back to the time-value pairs to send into an external tool. </p><p>These tools can then use the simplified query instead of doing their own custom analysis on your data.</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/01/Screen-Shot-2021-01-20-at-3.19.57-PM.png" class="kg-image" alt="Grafana dashboard UI, showing initial and downsampled data" loading="lazy" width="1600" height="944" srcset="https://timescale.ghost.io/blog/content/images/size/w600/2022/01/Screen-Shot-2021-01-20-at-3.19.57-PM.png 600w, https://timescale.ghost.io/blog/content/images/size/w1000/2022/01/Screen-Shot-2021-01-20-at-3.19.57-PM.png 1000w, https://timescale.ghost.io/blog/content/images/2022/01/Screen-Shot-2021-01-20-at-3.19.57-PM.png 1600w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://timescale.ghost.io/blog/content/images/2022/01/Screen-Shot-2021-01-20-at-3.18.43-PM.png" class="kg-image" alt="Grafana dashboard UI, showing initial and downsampled data" loading="lazy" width="1600" height="944" srcset="https://timescale.ghost.io/blog/content/images/size/w600/2022/01/Screen-Shot-2021-01-20-at-3.18.43-PM.png 600w, https://timescale.ghost.io/blog/content/images/size/w1000/2022/01/Screen-Shot-2021-01-20-at-3.18.43-PM.png 1000w, https://timescale.ghost.io/blog/content/images/2022/01/Screen-Shot-2021-01-20-at-3.18.43-PM.png 1600w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Example downsampling data from </span><a href="http://ecg.mit.edu/time-series/"><span style="white-space: pre-wrap;">this dataset</span></a><span style="white-space: pre-wrap;">. It keeps the large-scale features of the data, with an order of magnitude fewer data points</span></figcaption></figure><h2 id="statistical-sketching"><br>Statistical Sketching</h2><p>Sketching algorithms, such as <a href="https://github.com/timescale/timescale-analytics/issues/1">t-digest</a>, <a href="https://github.com/timescale/timescale-analytics/issues/3">hyperloglog</a>, and <a href="https://github.com/timescale/timescale-analytics/issues/6">count-min</a>, allow us to get a quick, approximate, answer for certain queries when the statistical bounds provided are acceptable.</p><p> This is even more exciting in the TimescaleDB ecosystem since it appears most of these sketches will fit nicely into <a href="https://docs.timescale.com/latest/using-timescaledb/continuous-aggregates">continuous aggregates</a>, allowing incredibly low query latency.  </p><p>For instance, a continuous aggregate displaying the daily unique visitors to a website could be defined like:</p><pre><code class="language-SQL">CREATE MATERIALIZED VIEW unique_vistors
WITH (timescaledb.continuous) AS
    SELECT 
    time_bucket('1 day', time) as day, 
    hll(visitor_id) as visitors
    FROM connections
    GROUP BY time_bucket('1 day', time);
</code></pre><p></p><p>Such a view could be queried to get the visitors over range of days, like so:</p><pre><code class="language-SQL">SELECT day, approx_distinct(visitors)
FROM unique_vistors
WHERE day &gt;= '2020-01-01' AND day &gt;= '2020-01-15'
</code></pre><p></p><p>Additionally, it would allow for re-aggregation to determine the number of unique visitors over a coarser time range, such as the number of monthly visitors: </p><pre><code class="language-SQL">SELECT time_bucket(day, '30 days'), approx_distinct(hll(visitors))
FROM unique_vistors
GROUP BY time_bucket(day, '30 days')
</code></pre><p></p><h2 id="pipelining">Pipelining</h2><p>SQL queries can get long, especially when there are multiple layers of aggregation and function calls.</p><p>For instance, to write a pairwise delta at minute-granularity in TimescaleDB, we’d use something like:</p><pre><code class="language-SQL">SELECT minutes, sampled - lag(sampled) OVER (ORDER BY minutes) as delta
FROM (
    SELECT
        time_bucket_gapfill(time, '1 minute') minutes,
        interpolate(first(value, time)) sampled
    FROM data
    GROUP BY time_bucket_gapfill(time, '1 minute')
) interpolated;
</code></pre><p></p><p>To mitigate this, the Timescale Analytics proposal includes a unified <a href="https://github.com/timescale/timescale-analytics/discussions/10">pipeline API</a> capability that would allow us to use the much more straightforward (and elegant) query below:</p><pre><code class="language-SQL">SELECT timeseries(time, value) |&gt; sample('1 minute') |&gt; interpolate('linear') |&gt; delta() FROM data;</code></pre><p></p><p>Besides the simpler syntax, this API could also enable some powerful optimizations, such as incremental pipelines, single-pass processing, and vectorization. </p><p>This is still very much in the design phase, and we’re currently having discussions about what such an API should <a href="https://github.com/timescale/timescale-analytics/discussions/10">look like</a>, what pipeline elements <a href="https://github.com/timescale/timescale-analytics/discussions/26">are appropriate</a>, and what the textual format <a href="https://github.com/timescale/timescale-analytics/discussions/10#discussioncomment-282898">should be</a>.</p><h2 id="how-we%E2%80%99re-building-timescale-analytics">How we’re building Timescale Analytics</h2><p>We’re building Timescale Analytics as a <a href="https://www.tigerdata.com/blog/top-8-postgresql-extensions" rel="noreferrer">PostgreSQL extension</a>. PostgreSQL's extension framework is quite powerful and allows for different levels of integration with database internals. </p><p>Timescale Analytics will be separate from the core  TimescaleDB extension. This is because TimescaleDB core interfaces quite deeply into PostgreSQL’s internals— including the planner, executor, and DDL interfaces—due to the demands of time-series data storage. This necessitates a certain conservatism to its development process in order to ensure that updating TimescaleDB versions cannot damage existing databases, and that features interact appropriately with PostgreSQL’s core functionality.</p><p>By separating the new analytics functionality into a dedicated Timescale Analytics extension, we can vastly reduce the contact area for these new functions, enabling us to move faster without increased risk. We will be focusing on improvements that take advantage of the PostgreSQL extension hooks for creating functions, aggregates, operators, and other database objects, rather those that require interfacing with the lower-level planning and execution infrastructure. Creating a separate extension also allows us to experiment with our build process and technologies, for instance, writing the extension <a href="https://github.com/zombodb/pgx">in Rust</a>.</p><p>More importantly,  we hope using a separate extension will lower barriers for community contributions. We know that the complexity of our integrations with PostgreSQL can make it difficult to contribute to TimescaleDB proper. We believe this new project will allow for much more self-contained contributions by avoiding projects requiring deep integration with the PostgreSQL planner or executor.</p><p>So, if you’ve been wanting to contribute back but didn’t know how or are a Rustacean looking to get involved in databasing, please join us!</p><h2 id="get-involved">Get Involved</h2><p>Before the code is written is the perfect time to have a say in where the project will go. To this end, we want—and need—your feedback: what are the frustrating parts of analyzing time-series data? What takes far more code than you feel it should? What runs slowly or only runs quickly after seemingly arcane rewrites?</p><p>We want to solve community-wide problems and incorporate as much feedback as possible, in addition to relying on our intuition, observation, and experiences.</p><p><strong>Want to help? </strong>You can submit suggestions and help shape the direction in 3 primary ways:</p><ul><li><a href="https://github.com/timescale/timescale-analytics/discussions"><strong>Look at some of the discussions</strong></a> we’re having right now and weigh in with your opinions. Any and all comments are welcome, whether you’re an experienced developer or just learning.</li><li><a href="https://github.com/timescale/timescale-analytics/labels/proposed-feature"><strong>Check out the features</strong></a> we’re thinking of adding, and weigh in on if they’re something you want, if we’re missing something, or if there are any issues or alternatives. We are releasing nightly Docker images of our builds.</li><li><a href="https://github.com/timescale/timescale-analytics/labels/feature-request"><strong>Explore our running feature requests, add a +1, </strong></a><strong> and </strong><a href="https://github.com/timescale/timescale-analytics/issues/new?assignees=&amp;labels=feature-request&amp;template=feature-request.md&amp;title="><strong>contribute your own</strong></a>.</li></ul><p><strong>Most importantly: </strong><a href="https://github.com/timescale/timescale-analytics/discussions"><strong>share your problems</strong></a><strong>! </strong>Tell us the kinds of queries or analyses you wish were easier, the issues you run into, or the workarounds you’ve created to solve gaps. (Example datasets are especially helpful, as they concretize your problems and create a shared language in which to discuss them.)</p><p><br></p>]]></content:encoded>
        </item>
    </channel>
</rss>