{"slug":"setup-timescaledb-hypertables","title":"setup-timescaledb-hypertables","summary":"Use this skill when creating database schemas or tables for Timescale, TimescaleDB, TigerData, or Tiger Cloud, especially for time-series, IoT, metrics, events, or log data. Use this to improve the performance of any insert-heavy table. **Trigger when user asks to:** - Create or ","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-02T16:12:26.409995Z","repo":{"url":"https://github.com/timescale/pg-aiguide","stars":1850,"forks":111,"license":"Apache-2.0","updatedAt":"2026-09-25T17:35:15Z"},"bodyHtml":"<hr>\n<p>name: setup-timescaledb-hypertables\ndescription: |\nUse this skill when creating database schemas or tables for Timescale, TimescaleDB, TigerData, or Tiger Cloud, especially for time-series, IoT, metrics, events, or log data. Use this to improve the performance of any insert-heavy table.</p>\n<p><strong>Trigger when user asks to:</strong></p>\n<ul>\n<li>Create or design SQL schemas/tables AND Timescale/TimescaleDB/TigerData/Tiger Cloud is available</li>\n<li>Set up hypertables, compression, retention policies, or continuous aggregates</li>\n<li>Configure partition columns, segment_by, order_by, or chunk intervals</li>\n<li>Optimize time-series database performance or storage</li>\n<li>Create tables for sensors, metrics, telemetry, events, or transaction logs</li>\n</ul>\n<p><strong>Keywords:</strong> CREATE TABLE, hypertable, Timescale, TimescaleDB, time-series, IoT, metrics, sensor data, compression policy, continuous aggregates, columnstore, retention policy, chunk interval, segment_by, order_by</p>\n<h2>Step-by-step instructions for hypertable creation, column selection, compression policies, retention, continuous aggregates, and indexes.\nlicense: Apache-2.0\ncompatibility: Requires PostgreSQL 15+ with TimescaleDB\nmetadata:\nauthor: tigerdata</h2>\n<h1>TimescaleDB Complete Setup</h1>\n<p>Instructions for insert-heavy data patterns where data is inserted but rarely changed:</p>\n<ul>\n<li><strong>Time-series data</strong> (sensors, metrics, system monitoring)</li>\n<li><strong>Event logs</strong> (user events, audit trails, application logs)</li>\n<li><strong>Transaction records</strong> (orders, payments, financial transactions)</li>\n<li><strong>Sequential data</strong> (records with auto-incrementing IDs and timestamps)</li>\n<li><strong>Append-only datasets</strong> (immutable records, historical data)</li>\n</ul>\n<h2>Step 1: Create Hypertable</h2>\n<pre><code>CREATE TABLE your_table_name (\n    timestamp TIMESTAMPTZ NOT NULL,\n    entity_id TEXT NOT NULL,          -- device_id, user_id, symbol, etc.\n    category TEXT,                    -- sensor_type, event_type, asset_class, etc.\n    value_1 DOUBLE PRECISION,         -- price, temperature, latency, etc.\n    value_2 DOUBLE PRECISION,         -- volume, humidity, throughput, etc.\n    value_3 INTEGER,                  -- count, status, level, etc.\n    metadata JSONB                    -- flexible additional data\n) WITH (\n    tsdb.hypertable,\n    tsdb.partition_column='timestamp',\n    tsdb.enable_columnstore=true,     -- Disable if table has vector columns\n    tsdb.segmentby='entity_id',       -- See selection guide below\n    tsdb.orderby='timestamp DESC',     -- See selection guide below\n    tsdb.sparse_index='minmax(value_1),minmax(value_2),minmax(value_3)' -- see selection guide below\n);\n</code></pre>\n<h3>Compression Decision</h3>\n<ul>\n<li><strong>Enable by default</strong> for insert-heavy patterns</li>\n<li><strong>Disable</strong> if table has vector type columns (pgvector) - indexes on vector columns incompatible with columnstore</li>\n</ul>\n<h3>Partition Column Selection</h3>\n<p>Must be time-based (TIMESTAMP/TIMESTAMPTZ/DATE) or integer (INT/BIGINT) with good temporal/sequential distribution.</p>\n<p><strong>Common patterns:</strong></p>\n<ul>\n<li>TIME-SERIES: <code>timestamp</code>, <code>event_time</code>, <code>measured_at</code></li>\n<li>EVENT LOGS: <code>event_time</code>, <code>created_at</code>, <code>logged_at</code></li>\n<li>TRANSACTIONS: <code>created_at</code>, <code>transaction_time</code>, <code>processed_at</code></li>\n<li>SEQUENTIAL: <code>id</code> (auto-increment when no timestamp), <code>sequence_number</code></li>\n<li>APPEND-ONLY: <code>created_at</code>, <code>inserted_at</code>, <code>id</code></li>\n</ul>\n<p><strong>Less ideal:</strong> <code>ingested_at</code> (when data entered system - use only if it's your primary query dimension)\n<strong>Avoid:</strong> <code>updated_at</code> (breaks time ordering unless it's primary query dimension)</p>\n<h3>Segment_By Column Selection</h3>\n<p><strong>PREFER SINGLE COLUMN</strong> - multi-column rarely optimal. Multi-column can only work for highly correlated columns (e.g., metric_name + metric_type) with sufficient row density.</p>\n<p><strong>Requirements:</strong></p>\n<ul>\n<li>Frequently used in WHERE clauses (most common filter)</li>\n<li>Good row density (&gt;100 rows per value per chunk)</li>\n<li>Primary logical partition/grouping</li>\n</ul>\n<p><strong>Examples:</strong></p>\n<ul>\n<li>IoT: <code>device_id</code></li>\n<li>Finance: <code>symbol</code></li>\n<li>Metrics: <code>service_name</code>, <code>service_name, metric_type</code> (if sufficient row density), <code>metric_name, metric_type</code> (if sufficient row density)</li>\n<li>Analytics: <code>user_id</code> if sufficient row density, otherwise <code>session_id</code></li>\n<li>E-commerce: <code>product_id</code> if sufficient row density, otherwise <code>category_id</code></li>\n</ul>\n<p><strong>Row density guidelines:</strong></p>\n<ul>\n<li>Target: &gt;100 rows per segment_by value within each chunk.</li>\n<li>Poor: &lt;10 rows per segment_by value per chunk → choose less granular column</li>\n<li>What to do with low-density columns: prepend to order_by column list.</li>\n</ul>\n<p><strong>Query pattern drives choice:</strong></p>\n<pre><code>SELECT * FROM table WHERE entity_id = 'X' AND timestamp &gt; ...\n-- ↳ segment_by: entity_id (if &gt;100 rows per chunk)\n</code></pre>\n<p><strong>Avoid:</strong> timestamps, unique IDs, low-density columns (&lt;100 rows/value/chunk), columns rarely used in filtering</p>\n<h3>Order_By Column Selection</h3>\n<p>Creates natural time-series progression when combined with segment_by for optimal compression.</p>\n<p><strong>Most common:</strong> <code>timestamp DESC</code></p>\n<p><strong>Examples:</strong></p>\n<ul>\n<li>IoT/Finance/E-commerce: <code>timestamp DESC</code></li>\n<li>Metrics: <code>metric_name, timestamp DESC</code> (if metric_name has too low density for segment_by)</li>\n<li>Analytics: <code>user_id, timestamp DESC</code> (user_id has too low density for segment_by)</li>\n</ul>\n<p><strong>Alternative patterns:</strong></p>\n<ul>\n<li><code>sequence_id DESC</code> for event streams with sequence numbers</li>\n<li><code>timestamp DESC, event_order DESC</code> for sub-ordering within same timestamp</li>\n</ul>\n<p><strong>Low-density column handling:</strong>\nIf a column has &lt;100 rows per chunk (too low for segment_by), prepend it to order_by:</p>\n<ul>\n<li>Example: <code>metric_name</code> has 20 rows/chunk → use <code>segment_by='service_name'</code>, <code>order_by='metric_name, timestamp DESC'</code></li>\n<li>Groups similar values together (all temperature readings, then pressure readings) for better compression</li>\n</ul>\n<p><strong>Good test:</strong> ordering created by <code>(segment_by_column, order_by_column)</code> should form a natural time-series progression. Values close to each other in the progression should be similar.</p>\n<p><strong>Avoid in order_by:</strong> random columns, columns with high variance between adjacent rows, columns unrelated to segment_by</p>\n<h3>Compression Sparse Index Selection</h3>\n<p><strong>Sparse indexes</strong> enable query filtering on compressed data without decompression. Store metadata per batch (~1000 rows) to eliminate batches that don't match query predicates.</p>\n<p><strong>Types:</strong></p>\n<ul>\n<li><strong>minmax:</strong> Min/max values per batch - for range queries (&gt;, &lt;, BETWEEN) on numeric/temporal columns</li>\n</ul>\n<p><strong>Use minmax for:</strong> price, temperature, measurement, timestamp (range filtering)</p>\n<p><strong>Use for:</strong></p>\n<ul>\n<li>minmax for outlier detection (temperature &gt; 90).</li>\n<li>minmax for fields that are highly correlated with segmentby and orderby columns (e.g. if orderby includes <code>created_at</code>, minmax on <code>updated_at</code> is useful).</li>\n</ul>\n<p><strong>Avoid:</strong> rarely filtered columns.</p>\n<p>IMPORTANT: NEVER index columns in segmentby or orderby. Orderby columns will always have minmax indexes without any configuration.</p>\n<p><strong>Configuration:</strong>\nThe format is a comma-separated list of type_of_index(column_name).</p>\n<pre><code>ALTER TABLE table_name SET (\n    timescaledb.sparse_index = 'minmax(value_1),minmax(value_2)'\n);\n</code></pre>\n<p>Explicit configuration available since v2.22.0 (was auto-created since v2.16.0).</p>\n<h3>Chunk Time Interval (Optional)</h3>\n<p>Default: 7 days (use if volume unknown, or ask user). Adjust based on volume:</p>\n<ul>\n<li>High frequency: 1 hour - 1 day</li>\n<li>Medium: 1 day - 1 week</li>\n<li>Low: 1 week - 1 month</li>\n</ul>\n<pre><code>SELECT set_chunk_time_interval('your_table_name', INTERVAL '1 day');\n</code></pre>\n<p><strong>Good test:</strong> recent chunk indexes should fit in less than 25% of RAM.</p>\n<h3>Indexes &amp; Primary Keys</h3>\n<p>Common index patterns - composite indexes on an id and timestamp:</p>\n<pre><code>CREATE INDEX idx_entity_timestamp ON your_table_name (entity_id, timestamp DESC);\n</code></pre>\n<p><strong>Important:</strong> Only create indexes you'll actually use - each has maintenance overhead.</p>\n<p><strong>Primary key and unique constraints rules:</strong> Must include partition column.</p>\n<p><strong>Option 1: Composite PK with partition column</strong></p>\n<pre><code>ALTER TABLE your_table_name ADD PRIMARY KEY (entity_id, timestamp);\n</code></pre>\n<p><strong>Option 2: Single-column PK (only if it's the partition column)</strong></p>\n<pre><code>CREATE TABLE ... (id BIGINT PRIMARY KEY, ...) WITH (tsdb.partition_column='id');\n</code></pre>\n<p><strong>Option 3: No PK</strong>: strict uniqueness is often not required for insert-heavy patterns.</p>\n<h2>Step 2: Compression Policy (Optional)</h2>\n<p><strong>IMPORTANT</strong>: If you used <code>tsdb.enable_columnstore=true</code> in Step 1, starting with TimescaleDB version 2.23 a columnstore policy is <strong>automatically created</strong> with <code>after =&gt; INTERVAL '7 days'</code>. You only need to call <code>add_columnstore_policy()</code> if you want to customize the <code>after</code> interval to something other than 7 days.</p>\n<p>Set <code>after</code> interval for when: data becomes mostly immutable (some updates/backfill OK) AND B-tree indexes aren't needed for queries (less common criterion).</p>\n<pre><code>-- In TimescaleDB 2.23 and later only needed if you want to override the default 7-day policy created by tsdb.enable_columnstore=true\n-- Remove the existing auto-created policy first:\n-- CALL remove_columnstore_policy('your_table_name');\n-- Then add custom policy:\n-- CALL add_columnstore_policy('your_table_name', after =&gt; INTERVAL '1 day');\n</code></pre>\n<h2>Step 3: Retention Policy</h2>\n<p>IMPORTANT: Don't guess - ask user or comment out if unknown.</p>\n<pre><code>-- Example - replace with requirements or comment out\nSELECT add_retention_policy('your_table_name', INTERVAL '365 days');\n</code></pre>\n<h2>Step 4: Create Continuous Aggregates</h2>\n<p>Use different aggregation intervals for different uses.</p>\n<h3>Short-term (Minutes/Hours)</h3>\n<p>For up-to-the-minute dashboards on high-frequency data.</p>\n<pre><code>CREATE MATERIALIZED VIEW your_table_hourly\nWITH (timescaledb.continuous) AS\nSELECT\n    time_bucket(INTERVAL '1 hour', timestamp) AS bucket,\n    entity_id,\n    category,\n    COUNT(*) as record_count,\n    AVG(value_1) as avg_value_1,\n    MIN(value_1) as min_value_1,\n    MAX(value_1) as max_value_1,\n    SUM(value_2) as sum_value_2\nFROM your_table_name\nGROUP BY bucket, entity_id, category;\n</code></pre>\n<h3>Long-term (Days/Weeks/Months)</h3>\n<p>For long-term reporting and analytics.</p>\n<pre><code>CREATE MATERIALIZED VIEW your_table_daily\nWITH (timescaledb.continuous) AS\nSELECT\n    time_bucket(INTERVAL '1 day', timestamp) AS bucket,\n    entity_id,\n    category,\n    COUNT(*) as record_count,\n    AVG(value_1) as avg_value_1,\n    MIN(value_1) as min_value_1,\n    MAX(value_1) as max_value_1,\n    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY value_1) as median_value_1,\n    PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY value_1) as p95_value_1,\n    SUM(value_2) as sum_value_2\nFROM your_table_name\nGROUP BY bucket, entity_id, category;\n</code></pre>\n<h2>Step 5: Aggregate Refresh Policies</h2>\n<p>Set up refresh policies based on your data freshness requirements.</p>\n<p><strong>start_offset:</strong> Usually omit (refreshes all). Exception: If you don't care about refreshing data older than X (see below). With retention policy on raw data: match the retention policy.</p>\n<p><strong>end_offset:</strong> Set beyond active update window (e.g., 15 min if data usually arrives within 10 min). Data newer than end_offset won't appear in queries without real-time aggregation. If you don't know your update window, use the size of the time_bucket in the query, but not less than 5 minutes.</p>\n<p><strong>schedule_interval:</strong> Set to the same value as the end_offset but not more than 1 hour.</p>\n<p><strong>Hourly - frequent refresh for dashboards:</strong></p>\n<pre><code>SELECT add_continuous_aggregate_policy('your_table_hourly',\n    start_offset =&gt; NULL,\n    end_offset =&gt; INTERVAL '15 minutes',\n    schedule_interval =&gt; INTERVAL '15 minutes');\n</code></pre>\n<p><strong>Daily - less frequent for reports:</strong></p>\n<pre><code>SELECT add_continuous_aggregate_policy('your_table_daily',\n    start_offset =&gt; NULL,\n    end_offset =&gt; INTERVAL '1 hour',\n    schedule_interval =&gt; INTERVAL '1 hour');\n</code></pre>\n<p><strong>Use start_offset only if you don't care about refreshing old data</strong>\nUse for high-volume systems where query accuracy on older data doesn't matter:</p>\n<pre><code>-- the following aggregate can be stale for data older than 7 days\n-- SELECT add_continuous_aggregate_policy('aggregate_for_last_7_days',\n--     start_offset =&gt; INTERVAL '7 days',    -- only refresh last 7 days (NULL = refresh all)\n--     end_offset =&gt; INTERVAL '15 minutes',\n--     schedule_interval =&gt; INTERVAL '15 minutes');\n</code></pre>\n<p>IMPORTANT: you MUST set a start_offset to be less than the retention policy on raw data. By default, set the start_offset equal to the retention policy.\nIf the retention policy is commented out, comment out the start_offset as well. like this:</p>\n<pre><code>SELECT add_continuous_aggregate_policy('your_table_daily',\n    start_offset =&gt; NULL,    -- Use NULL to refresh all data, or set to retention period if enabled on raw data\n--  start_offset =&gt; INTERVAL '&lt;retention period here&gt;',    -- uncomment if retention policy is enabled on the raw data table\n    end_offset =&gt; INTERVAL '1 hour',\n    schedule_interval =&gt; INTERVAL '1 hour');\n</code></pre>\n<h2>Step 6: Real-Time Aggregation (Optional)</h2>\n<p>Real-time combines materialized + recent raw data at query time. Provides up-to-date results at the cost of higher query latency.</p>\n<p>More useful for fine-grained aggregates (e.g., minutely) than coarse ones (e.g., daily/monthly) since large buckets will be mostly incomplete with recent data anyway.</p>\n<p>Disabled by default in v2.13+, before that it was enabled by default.</p>\n<p><strong>Use when:</strong> Need data newer than end_offset, up-to-minute dashboards, can tolerate higher query latency\n<strong>Disable when:</strong> Performance critical, refresh policies sufficient, high query volume, missing and stale data for recent data is acceptable</p>\n<p><strong>Enable for current results (higher query cost):</strong></p>\n<pre><code>ALTER MATERIALIZED VIEW your_table_hourly SET (timescaledb.materialized_only = false);\n</code></pre>\n<p><strong>Disable for performance (but with stale results):</strong></p>\n<pre><code>ALTER MATERIALIZED VIEW your_table_hourly SET (timescaledb.materialized_only = true);\n</code></pre>\n<h2>Step 7: Compress Aggregates</h2>\n<p>Rule: segment_by = ALL GROUP BY columns except time_bucket, order_by = time_bucket DESC</p>\n<pre><code>-- Hourly\nALTER MATERIALIZED VIEW your_table_hourly SET (\n    timescaledb.enable_columnstore,\n    timescaledb.segmentby = 'entity_id, category',\n    timescaledb.orderby = 'bucket DESC'\n);\nCALL add_columnstore_policy('your_table_hourly', after =&gt; INTERVAL '3 days');\n\n-- Daily\nALTER MATERIALIZED VIEW your_table_daily SET (\n    timescaledb.enable_columnstore,\n    timescaledb.segmentby = 'entity_id, category',\n    timescaledb.orderby = 'bucket DESC'\n);\nCALL add_columnstore_policy('your_table_daily', after =&gt; INTERVAL '7 days');\n</code></pre>\n<h2>Step 8: Aggregate Retention</h2>\n<p>Aggregates are typically kept longer than raw data.\nIMPORTANT: Don't guess - ask user or you <strong>MUST comment out if unknown</strong>.</p>\n<pre><code>-- Example - replace or comment out\nSELECT add_retention_policy('your_table_hourly', INTERVAL '2 years');\nSELECT add_retention_policy('your_table_daily', INTERVAL '5 years');\n</code></pre>\n<h2>Step 9: Performance Indexes on Continuous Aggregates</h2>\n<p><strong>Index strategy:</strong> Analyze WHERE clauses in common queries → Create indexes matching filter columns + time ordering</p>\n<p><strong>Pattern:</strong> <code>(filter_column, bucket DESC)</code> supports <code>WHERE filter_column = X AND bucket &gt;= Y ORDER BY bucket DESC</code></p>\n<p>Examples:</p>\n<pre><code>CREATE INDEX idx_hourly_entity_bucket ON your_table_hourly (entity_id, bucket DESC);\nCREATE INDEX idx_hourly_category_bucket ON your_table_hourly (category, bucket DESC);\n</code></pre>\n<p><strong>Multi-column filters:</strong> Create composite indexes for <code>WHERE entity_id = X AND category = Y</code>:</p>\n<pre><code>CREATE INDEX idx_hourly_entity_category_bucket ON your_table_hourly (entity_id, category, bucket DESC);\n</code></pre>\n<p><strong>Important:</strong> Only create indexes you'll actually use - each has maintenance overhead.</p>\n<h2>Step 10: Optional Enhancements</h2>\n<h3>Space Partitioning (NOT RECOMMENDED)</h3>\n<p>Only for query patterns where you ALWAYS filter by the space-partition column with expert knowledge and extensive benchmarking. STRONGLY prefer time-only partitioning.</p>\n<h2>Step 11: Verify Configuration</h2>\n<pre><code>-- Check hypertable\nSELECT * FROM timescaledb_information.hypertables\nWHERE hypertable_name = 'your_table_name';\n\n-- Check compression settings\nSELECT * FROM hypertable_compression_stats('your_table_name');\n\n-- Check aggregates\nSELECT * FROM timescaledb_information.continuous_aggregates;\n\n-- Check policies\nSELECT * FROM timescaledb_information.jobs ORDER BY job_id;\n\n-- Monitor chunk information\nSELECT\n    chunk_name,\n    range_start,\n    range_end,\n    is_compressed\nFROM timescaledb_information.chunks\nWHERE hypertable_name = 'your_table_name'\nORDER BY range_start DESC;\n</code></pre>\n<h2>Performance Guidelines</h2>\n<ul>\n<li><strong>Chunk size:</strong> Recent chunk indexes should fit in less than 25% of RAM</li>\n<li><strong>Compression:</strong> Expect 90%+ reduction (10x) with proper columnstore config</li>\n<li><strong>Query optimization:</strong> Use continuous aggregates for historical queries and dashboards</li>\n<li><strong>Memory:</strong> Run <code>timescaledb-tune</code> for self-hosting (auto-configured on cloud)</li>\n</ul>\n<h2>Schema Best Practices</h2>\n<h3>Do's and Don'ts</h3>\n<ul>\n<li>✅ Use <code>TIMESTAMPTZ</code> NOT <code>timestamp</code></li>\n<li>✅ Use <code>&gt;=</code> and <code>&lt;</code> NOT <code>BETWEEN</code> for timestamps</li>\n<li>✅ Use <code>TEXT</code> with constraints NOT <code>char(n)</code>/<code>varchar(n)</code></li>\n<li>✅ Use <code>snake_case</code> NOT <code>CamelCase</code></li>\n<li>✅ Use <code>BIGINT GENERATED ALWAYS AS IDENTITY</code> NOT <code>SERIAL</code></li>\n<li>✅ Use <code>BIGINT</code> for IDs by default over <code>INTEGER</code> or <code>SMALLINT</code></li>\n<li>✅ Use <code>DOUBLE PRECISION</code> by default over <code>REAL</code>/<code>FLOAT</code></li>\n<li>✅ Use <code>NUMERIC</code> NOT <code>MONEY</code></li>\n<li>✅ Use <code>NOT EXISTS</code> NOT <code>NOT IN</code></li>\n<li>✅ Use <code>time_bucket()</code> or <code>date_trunc()</code> NOT <code>timestamp(0)</code> for truncation</li>\n</ul>\n<h2>API Reference (Current vs Deprecated)</h2>\n<p><strong>Deprecated Parameters → New Parameters:</strong></p>\n<ul>\n<li><code>timescaledb.compress</code> → <code>timescaledb.enable_columnstore</code></li>\n<li><code>timescaledb.compress_segmentby</code> → <code>timescaledb.segmentby</code></li>\n<li><code>timescaledb.compress_orderby</code> → <code>timescaledb.orderby</code></li>\n</ul>\n<p><strong>Deprecated Functions → New Functions:</strong></p>\n<ul>\n<li><code>add_compression_policy()</code> → <code>add_columnstore_policy()</code></li>\n<li><code>remove_compression_policy()</code> → <code>remove_columnstore_policy()</code></li>\n<li><code>compress_chunk()</code> → <code>convert_to_columnstore()</code> (use with <code>CALL</code>, not <code>SELECT</code>)</li>\n<li><code>decompress_chunk()</code> → <code>convert_to_rowstore()</code> (use with <code>CALL</code>, not <code>SELECT</code>)</li>\n</ul>\n<p><strong>Compression Stats (use functions, not views):</strong></p>\n<ul>\n<li>Use function: <code>hypertable_compression_stats('table_name')</code></li>\n<li>Use function: <code>chunk_compression_stats('_timescaledb_internal._hyper_X_Y_chunk')</code></li>\n<li>Note: Views like <code>columnstore_settings</code> may not be available in all versions; use functions instead</li>\n</ul>\n<p><strong>Manual Compression Example:</strong></p>\n<pre><code>-- Compress a specific chunk\nCALL convert_to_columnstore('_timescaledb_internal._hyper_7_1_chunk');\n\n-- Check compression statistics\nSELECT\n    number_compressed_chunks,\n    pg_size_pretty(before_compression_total_bytes) as before_compression,\n    pg_size_pretty(after_compression_total_bytes) as after_compression,\n    ROUND(100.0 * (1 - after_compression_total_bytes::numeric / NULLIF(before_compression_total_bytes, 0)), 1) as compression_pct\nFROM hypertable_compression_stats('your_table_name');\n</code></pre>\n<h2>Questions to Ask User</h2>\n<ol>\n<li>What kind of data will you be storing?</li>\n<li>How do you expect to use the data?</li>\n<li>What queries will you run?</li>\n<li>How long to keep the data?</li>\n<li>Column types if unclear</li>\n</ol>\n","files":[{"path":"SKILL.md","sizeBytes":18850,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-02T16:12:37.50214Z","sha256":"B08522D9A9F04F12818295E4BB6141BEDB99929CDF8675F6452538A64BB23A2A","sizeBytes":6663},"review":null,"source":{"repositoryUrl":"https://github.com/timescale/pg-aiguide","path":"skills/setup-timescaledb-hypertables","license":"Apache-2.0","commit":"b236d3583fb51f5ef009d2c95d4fc361df748280","subtreeSha":"67D590247E21547896B9BF0704834C7CD0531905A88C606593C0D6AB50F592CF","lastSyncedAt":"2026-09-27T19:45:41.290646Z"},"reviewedAt":"2026-09-02T16:13:03.581068Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/timescale/pg-aiguide/tree/main/skills/setup-timescaledb-hypertables"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install timescale-pg-aiguide@llmmart"},{"target":"git","command":"git clone https://github.com/timescale/pg-aiguide.git"}]}