AutomationFlowsData & Sheets › Execute Queries

Execute Queries

Execute_Queries. Uses executeWorkflowTrigger, postgres. Event-driven trigger; 9 nodes.

Event trigger★★★★☆ complexity9 nodesExecute Workflow TriggerPostgres
Data & Sheets Trigger: Event Nodes: 9 Complexity: ★★★★☆ Added:

This workflow follows the Execute Workflow Trigger → Postgres recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "updatedAt": "2025-12-24T09:01:28.106Z",
  "createdAt": "2025-12-23T09:30:27.014Z",
  "id": "CgUAxK0i4YhrZ2Wp",
  "name": "Execute_Queries",
  "description": null,
  "active": false,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "inputSource": "passthrough"
      },
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        0
      ],
      "id": "trigger-execute-queries",
      "name": "ExecuteWorkflowTrigger"
    },
    {
      "parameters": {
        "jsCode": "// Validate input and initialize loop state\n// Execute_Queries: Unified sub-workflow for all database operations\n// - Executes queries sequentially (supports chaining via $results)\n// - Preserves ctx throughout\n// - Stores results in ctx.db[key] = { row, rows, count }\n\nconst item = $input.first();\nconst ctx = item.json.ctx;\n\n// If no queries or empty array, return state that will skip the loop gracefully\nif (!ctx?.db_queries || !Array.isArray(ctx.db_queries) || ctx.db_queries.length === 0) {\n  return [{\n    json: {\n      original_ctx: ctx || {},\n      queries: [],\n      current_index: 0,\n      results: {},\n      has_more: false\n    }\n  }];\n}\n\n// Validate each query has required fields\nfor (let i = 0; i < ctx.db_queries.length; i++) {\n  const q = ctx.db_queries[i];\n  if (!q.key) {\n    throw new Error(`Execute_Queries: Query at index ${i} missing 'key' field`);\n  }\n  if (!q.sql) {\n    throw new Error(`Execute_Queries: Query '${q.key}' at index ${i} missing 'sql' field`);\n  }\n}\n\n// Initialize loop state\nreturn [{\n  json: {\n    original_ctx: ctx,\n    queries: ctx.db_queries,\n    current_index: 0,\n    results: {},\n    has_more: true\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        224,
        0
      ],
      "id": "initialize-loop",
      "name": "InitializeLoop"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "has-more",
              "leftValue": "={{ $json.has_more }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        336,
        -96
      ],
      "id": "has-queries",
      "name": "HasQueries?"
    },
    {
      "parameters": {
        "jsCode": "// Prepare current query for execution\n// Supports $results.key.field references in params for chaining\n// Also formats arrays as PostgreSQL array literals when needed\nconst item = $input.first();\nconst state = item.json;\nconst query = state.queries[state.current_index];\n\n// Helper to format JS arrays as PostgreSQL array literals\n// Handles special characters: commas, braces, quotes, backslashes\nfunction formatPgArray(arr) {\n  if (!Array.isArray(arr)) return arr;\n  \n  const formatted = arr.map(item => {\n    if (Array.isArray(item)) {\n      return formatPgArray(item); // Recursive for nested arrays\n    }\n    if (typeof item === 'string') {\n      // Escape and quote strings with special characters\n      if (item.includes(',') || item.includes('{') || item.includes('}') || \n          item.includes('\"') || item.includes('\\\\') || item.includes(' ')) {\n        return '\"' + item.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"') + '\"';\n      }\n    }\n    return item;\n  });\n  \n  return '{' + formatted.join(',') + '}';\n}\n\n// Process params to resolve $results references\nlet params = query.params || [];\n\nif (params.length > 0) {\n  params = params.map(param => {\n    if (typeof param === 'string' && param.startsWith('$results.')) {\n      // Parse reference like \"$results.trace.row.id\"\n      const path = param.substring('$results.'.length).split('.');\n      let value = state.results;\n      for (const key of path) {\n        if (value && typeof value === 'object') {\n          value = value[key];\n        } else {\n          value = undefined;\n          break;\n        }\n      }\n      if (value === undefined) {\n        throw new Error(`Execute_Queries: Could not resolve reference ${param}`);\n      }\n      // Auto-format arrays for PostgreSQL\n      return formatPgArray(value);\n    }\n    // Also format arrays that aren't references\n    if (Array.isArray(param)) {\n      return formatPgArray(param);\n    }\n    return param;\n  });\n}\n\nreturn [{\n  json: {\n    ...state,\n    current_query: query,\n    sql: query.sql,\n    params: params\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        448,
        0
      ],
      "id": "prepare-query",
      "name": "PrepareQuery"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "{{ $json.sql }}",
        "options": {
          "queryReplacement": "={{ $json.params }}"
        }
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        672,
        0
      ],
      "id": "execute-postgres-query",
      "name": "ExecuteQuery",
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "append",
        "numberInputs": 2
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        880,
        0
      ],
      "id": "merge-results",
      "name": "MergeResults"
    },
    {
      "parameters": {
        "jsCode": "// Collect query results and check if more queries remain\nconst items = $input.all();\n\n// Find state from merge (has original_ctx)\nlet state = null;\nconst queryResults = [];\n\nfor (const item of items) {\n  if (item.json.original_ctx) {\n    state = item.json;\n  } else {\n    // This is a Postgres result row\n    queryResults.push(item.json);\n  }\n}\n\nif (!state) {\n  throw new Error('Execute_Queries: Lost loop state');\n}\n\n// Store results for current query\nconst currentKey = state.current_query.key;\nstate.results[currentKey] = {\n  row: queryResults[0] || null,  // First row (common for RETURNING or single-row SELECT)\n  rows: queryResults,             // All rows\n  count: queryResults.length      // Row count\n};\n\n// Move to next query\nstate.current_index++;\n\n// Check if more queries remain\nconst hasMore = state.current_index < state.queries.length;\n\nreturn [{\n  json: {\n    ...state,\n    has_more: hasMore\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1104,
        0
      ],
      "id": "collect-results",
      "name": "CollectResults"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "has-more",
              "leftValue": "={{ $json.has_more }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1328,
        0
      ],
      "id": "check-more-queries",
      "name": "MoreQueries?"
    },
    {
      "parameters": {
        "jsCode": "// Restore ctx with all query results in ctx.db namespace\nconst item = $input.first();\nconst state = item.json;\n\n// Remove db_queries since it was consumed\nconst { db_queries, ...restCtx } = state.original_ctx;\n\nreturn [{\n  json: {\n    ctx: {\n      ...restCtx,\n      db: {\n        ...(restCtx.db || {}),\n        ...state.results\n      }\n    }\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1552,
        112
      ],
      "id": "finalize-context",
      "name": "FinalizeContext"
    }
  ],
  "connections": {
    "ExecuteWorkflowTrigger": {
      "main": [
        [
          {
            "node": "InitializeLoop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "InitializeLoop": {
      "main": [
        [
          {
            "node": "HasQueries?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HasQueries?": {
      "main": [
        [
          {
            "node": "PrepareQuery",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "FinalizeContext",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PrepareQuery": {
      "main": [
        [
          {
            "node": "ExecuteQuery",
            "type": "main",
            "index": 0
          },
          {
            "node": "MergeResults",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "ExecuteQuery": {
      "main": [
        [
          {
            "node": "MergeResults",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "MergeResults": {
      "main": [
        [
          {
            "node": "CollectResults",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CollectResults": {
      "main": [
        [
          {
            "node": "MoreQueries?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "MoreQueries?": {
      "main": [
        [
          {
            "node": "PrepareQuery",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "FinalizeContext",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "staticData": null,
  "meta": null,
  "versionId": "7ed2d22e-978a-4d88-b66d-909616bed0e6",
  "activeVersionId": null,
  "versionCounter": 137,
  "triggerCount": 0,
  "shared": [
    {
      "updatedAt": "2025-12-23T09:30:27.014Z",
      "createdAt": "2025-12-23T09:30:27.014Z",
      "role": "workflow:owner",
      "workflowId": "CgUAxK0i4YhrZ2Wp",
      "projectId": "erM3nntdLL53noWi",
      "project": {
        "updatedAt": "2025-12-23T09:23:39.658Z",
        "createdAt": "2025-12-23T09:16:56.460Z",
        "id": "erM3nntdLL53noWi",
        "name": "Chris Irineo <chriskevini@gmail.com>",
        "type": "personal",
        "icon": null,
        "description": null,
        "projectRelations": [
          {
            "updatedAt": "2025-12-23T09:16:56.460Z",
            "createdAt": "2025-12-23T09:16:56.460Z",
            "userId": "2a851a2d-b7e5-4b3c-aefb-6eaaa79e0659",
            "projectId": "erM3nntdLL53noWi",
            "user": {
              "updatedAt": "2025-12-24T08:40:46.063Z",
              "createdAt": "2025-12-23T09:16:54.881Z",
              "id": "2a851a2d-b7e5-4b3c-aefb-6eaaa79e0659",
              "email": "chriskevini@gmail.com",
              "firstName": "Chris",
              "lastName": "Irineo",
              "personalizationAnswers": {
                "version": "v4",
                "personalization_survey_submitted_at": "2025-12-23T09:23:43.723Z",
                "personalization_survey_n8n_version": "1.123.5"
              },
              "settings": {
                "userActivated": true,
                "firstSuccessfulWorkflowId": "CgUAxK0i4YhrZ2Wp",
                "userActivatedAt": 1766487000077,
                "easyAIWorkflowOnboarded": true
              },
              "disabled": false,
              "mfaEnabled": false,
              "lastActiveAt": "2025-12-24",
              "isPending": false
            }
          }
        ]
      }
    }
  ],
  "tags": [],
  "activeVersion": null
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Execute_Queries. Uses executeWorkflowTrigger, postgres. Event-driven trigger; 9 nodes.

Source: https://github.com/chriskevini/kairon/blob/ab924f228ceb22522b9a4dfa1ab4589eb86273ad/n8n-workflows/Execute_Queries.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

Reagendamiento_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 89 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.

N8N Nodes Evolution Api, Redis, HTTP Request +3
Data & Sheets

Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

Save_Extraction. Uses executeWorkflowTrigger, postgres, httpRequest. Event-driven trigger; 22 nodes.

Execute Workflow Trigger, Postgres, HTTP Request
Data & Sheets

Youtube Searcher. Uses splitInBatches, httpRequest, manualTrigger, executeWorkflowTrigger. Event-driven trigger; 21 nodes.

HTTP Request, Execute Workflow Trigger, Postgres +1