diff --git a/apps/studio/components/interfaces/QueryInsights/QueryRowExplorer.tsx b/apps/studio/components/interfaces/QueryInsights/QueryRowExplorer.tsx
index 2445590e49d..f4a15eb7fff 100644
--- a/apps/studio/components/interfaces/QueryInsights/QueryRowExplorer.tsx
+++ b/apps/studio/components/interfaces/QueryInsights/QueryRowExplorer.tsx
@@ -6,6 +6,7 @@ import DataGrid, { Column, DataGridHandle, Row } from 'react-data-grid'
import { useParams } from 'common'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
+ Badge,
Button,
ResizableHandle,
ResizablePanel,
@@ -93,6 +94,54 @@ export const QueryRowExplorer = ({ startTime, endTime }: QueryRowExplorerProps)
)
},
},
+ {
+ key: 'slowness_rating',
+ name: 'Slowness Rating',
+ resizable: true,
+ minWidth: 120,
+ headerCellClass: 'cursor-pointer',
+ renderHeaderCell: () => {
+ return (
+
onSortChange('slowness_rating')}
+ >
+
+ {sort?.column === 'slowness_rating' && (
+ <>{sort.order === 'desc' ?
:
}>
+ )}
+
+ )
+ },
+ renderCell: (props) => {
+ const value = props.row?.slowness_rating
+ const getBadgeVariant = (rating: string) => {
+ switch (rating) {
+ case 'GREAT':
+ case 'ACCEPTABLE':
+ return 'default'
+ case 'NOTICEABLE':
+ case 'SLOW':
+ return 'warning'
+ case 'CRITICAL':
+ return 'destructive'
+ default:
+ return 'default'
+ }
+ }
+ return (
+
+
+
+ {value}
+
+
+
+ )
+ },
+ },
{
key: 'database',
name: 'Database',
@@ -304,6 +353,7 @@ export const QueryRowExplorer = ({ startTime, endTime }: QueryRowExplorerProps)
cmd_type_text: 'SELECT',
application_name: 'Test App',
badness_score: 25.5,
+ slowness_rating: 'GREAT',
error_count: 0,
startup_cost_before: 0,
startup_cost_after: 0,
diff --git a/apps/studio/data/query-insights/query-metrics-query.ts b/apps/studio/data/query-insights/query-metrics-query.ts
index 1141493d068..156b35745f9 100644
--- a/apps/studio/data/query-insights/query-metrics-query.ts
+++ b/apps/studio/data/query-insights/query-metrics-query.ts
@@ -38,6 +38,7 @@ export type QueryInsightsQuery = {
cmd_type_text: string
application_name: string
badness_score: number
+ slowness_rating: string
// index_advisor results
startup_cost_before?: number
startup_cost_after?: number
@@ -215,7 +216,15 @@ const getQueriesSql = (startTime: string, endTime: string) => /* SQL */ `
SUM(CASE WHEN cmd_type = 3 THEN rows ELSE 0 END) +
SUM(CASE WHEN cmd_type = 4 THEN rows ELSE 0 END),
1)
- ) AS badness_score
+ ) AS badness_score,
+ -- Slowness rating based on mean execution time
+ CASE
+ WHEN SUM(total_exec_time) / NULLIF(SUM(calls), 0) < 100 THEN 'GREAT'
+ WHEN SUM(total_exec_time) / NULLIF(SUM(calls), 0) < 200 THEN 'ACCEPTABLE'
+ WHEN SUM(total_exec_time) / NULLIF(SUM(calls), 0) < 1000 THEN 'NOTICEABLE'
+ WHEN SUM(total_exec_time) / NULLIF(SUM(calls), 0) < 5000 THEN 'SLOW'
+ ELSE 'CRITICAL'
+ END AS slowness_rating
FROM pg_stat_monitor
WHERE bucket_start_time >= '${startTime}'
AND bucket_start_time <= '${endTime}'
@@ -281,7 +290,15 @@ const getQueriesWithErrorsSql = (startTime: string, endTime: string) => /* SQL *
SUM(CASE WHEN cmd_type = 3 THEN rows ELSE 0 END) +
SUM(CASE WHEN cmd_type = 4 THEN rows ELSE 0 END),
1)
- ) AS badness_score
+ ) AS badness_score,
+ -- Slowness rating based on mean execution time
+ CASE
+ WHEN SUM(total_exec_time) / NULLIF(SUM(calls), 0) < 100 THEN 'GREAT'
+ WHEN SUM(total_exec_time) / NULLIF(SUM(calls), 0) < 200 THEN 'ACCEPTABLE'
+ WHEN SUM(total_exec_time) / NULLIF(SUM(calls), 0) < 1000 THEN 'NOTICEABLE'
+ WHEN SUM(total_exec_time) / NULLIF(SUM(calls), 0) < 5000 THEN 'SLOW'
+ ELSE 'CRITICAL'
+ END AS slowness_rating
FROM pg_stat_monitor
WHERE bucket_start_time >= '${startTime}'
AND bucket_start_time <= '${endTime}'