SQL Query Builder
Visually build SQL queries by selecting columns, tables, joins, and filters.
Assemble a SQL skeleton with visible decisions
The visual SQL Query Builder converts a small set of form choices into a MySQL-style statement. It supports four modes: SELECT, INSERT, UPDATE, and DELETE. As controls change, the read-only SQL Output updates automatically and always ends with a semicolon. This is useful for drafting a query shape, learning how clauses fit together, or creating a starting point for application code.
It is not a schema browser or no-code database client. There is no connection, execution, table discovery, join designer, parameter binding, or query validation. Backticks are added around table and column identifiers, values are inferred as numbers or quoted strings, and every filter is joined with AND. Those concrete rules make the generated SQL predictable, but they also define its limits.
Tour of the builder
The left panel begins in SELECT mode with table users and columns *. The query-type switch offers SELECT, INSERT, UPDATE, and DELETE. Table Name is available in every mode. SELECT additionally exposes Columns (Comma Separated), Order By, Direction (ASC or DESC), and numeric Limit.
The right-side Filters (WHERE Clauses) card is present for every mode. Add Condition creates a row containing a column field, an operator menu, a value field, and a trash icon. Operators are =, !=, >, <, LIKE, and IN. Multiple rows become AND predicates in insertion order. Copy places the current SQL output on the clipboard and shows Copied briefly.
Start with SELECT, enter trusted identifiers, add one condition at a time, and inspect output after each. Copy only when the statement represents the intended skeleton. Replace literal values with your database library’s placeholders before application use.
SELECT in detail
Comma-separated column input is split at commas. Every item other than exactly * after trimming receives backticks. For id, email, created_at, output begins:
SELECT `id`, `email`, `created_at` FROM `users`
An Order By value is also trimmed and backtick-quoted. Direction follows the selector. A nonempty Limit is converted to a number; if conversion produces zero or an invalid value, the generated limit falls back to 10. Leaving Limit empty omits the clause.
With a filter status = active, order column created_at, direction DESC, and limit 25, the result is:
SELECT * FROM `users` WHERE `status` = 'active' ORDER BY `created_at` DESC LIMIT 25;
There is no alias, expression, aggregate, grouping, offset, distinct, or join control. Typing COUNT(*) as a column wraps it in backticks as if it were an identifier, yielding unusable SQL. Qualified names such as users.id are wrapped as one backtick-delimited name rather than separately quoting each segment. Use simple identifiers here and add advanced syntax after copying.
INSERT and UPDATE are templates
Switching to INSERT ignores columns, filters, ordering, and limits. It emits a fixed placeholder:
INSERT INTO `users` (`column1`, `column2`) VALUES ('value1', 'value2');
The form does not provide fields for insert columns or values. Replace both placeholders manually and use bound parameters in program code.
UPDATE also starts with a fixed assignment, then appends any configured filters:
UPDATE `users` SET `column1` = 'value1' WHERE `id` = 42;
Conditions can be prepared while another mode is selected because filter state remains in the component. Verify the visible output whenever switching modes. There is no safety lock preventing an UPDATE without WHERE, and no UI for multiple assignments. Treat output as a draft rather than an executable migration.
DELETE deserves a deliberate pause
DELETE emits DELETE FROM plus the table and any filters. With no conditions it generates a full-table deletion:
DELETE FROM `users`;
The tool does not warn, disable Copy, or require confirmation. Before running a generated DELETE, first construct an equivalent SELECT with the same predicates and review returned rows. Execute inside a transaction where possible, verify affected-row counts, and retain a recovery path. UI convenience does not reduce the consequence of a missing tenant or status condition.
How filter values are inferred
For each condition, the builder asks JavaScript whether Number(value) is a number. Numeric-looking strings are emitted without quotes. Other text is wrapped in single quotes. This creates intuitive output for 42 but several surprising cases:
- An empty value converts to zero and is emitted as an empty unquoted position, producing malformed SQL.
- Numeric strings with leading zeros lose no source characters in output, but are treated as numbers semantically.
- Scientific notation, whitespace-padded numbers, and hexadecimal-like input may be classified numerically.
NULL,true, and date text are quoted as ordinary strings.- Apostrophes inside text are not escaped.
LIKE receives the same value treatment, so enter %admin% to generate LIKE '%admin%'. IN also receives the same single-value handling; entering 1,2,3 becomes the quoted string '1,2,3', not (1, 2, 3). The operator exists in the menu, but useful IN syntax requires manual correction after copying.
Blank condition columns become the literal placeholder column, without backticks. This lets the output remain visible while a row is unfinished, but it is not valid proof of completeness.
Identifier and value safety
The builder surrounds trimmed identifiers with backticks, which aligns most closely with MySQL and MariaDB. PostgreSQL commonly uses double quotes; SQL Server often uses brackets or configured quoting; other engines vary. A backtick inside user input is not escaped. Table input is trusted text embedded inside backticks, so the output must not be treated as securely sanitized SQL.
Quoted values are also not parameterized or escaped. A name such as O'Brien produces a broken literal. More importantly, accepting untrusted form values and executing generated text invites SQL injection. In application code, retain the selected table and column names from an allowlist, and bind values through the driver:
SELECT * FROM users WHERE status = ?
Placeholder style may be ?, $1, :status, or another form. Use the database library’s native API rather than interpolating copied values.
A reliable implementation workflow
Use the builder to communicate intent: choose operation, target, projection, predicates, ordering, and limit. Copy the resulting skeleton into code or a SQL editor. Then adapt identifier quoting and placeholders to the exact dialect. Add unsupported joins, grouping, aliases, expressions, conflict handling, or returning clauses by hand.
Run a syntax-aware formatter and linter. Test against a disposable database with representative schema and indexes. For SELECT, inspect an execution plan and ensure the limit and order express deterministic pagination. For UPDATE and DELETE, compare a SELECT of target rows and use a transaction. For INSERT, replace placeholders and verify constraints.
Code review should focus on semantics the form cannot see: tenant isolation, null behavior, collation, time zones, index use, transaction scope, and authorization. A visual SQL query builder reduces typing, not database reasoning.
Failure modes
Expect invalid or misleading output when columns contain expressions, identifiers are qualified, values include quotes, IN receives a list, or a dialect rejects backticks. Empty table input falls back to backticked table. Empty SELECT columns fall back to *. A typed limit of 0 becomes 10, so the preview may not match the user’s intention. Negative limits are emitted as negative numbers if accepted by the browser input, though many engines reject them.
Conditions survive mode switches and are used only by SELECT, UPDATE, and DELETE. ORDER BY and LIMIT are SELECT-only. INSERT is entirely fixed beyond the table. No mode supports OR grouping, parentheses, joins, subqueries, NULL predicates, BETWEEN, or prepared placeholders.
FAQ
Can the builder connect to my database?
No. It generates text only and has no schema or credentials.
Why is IN output quoted as one value?
All conditions use one generic value formatter. Edit the copied query to use parentheses and properly bound list values.
Can I build joins or grouped queries?
No. There are no join, GROUP BY, or HAVING controls.
What happens if Limit is zero?
A nonempty value converted to zero falls back to LIMIT 10. Leave it empty to omit the limit.
Is generated SQL safe from injection?
No. Identifiers and literal values are assembled as text. Use allowlisted identifiers and driver-bound parameters.
Do filters protect UPDATE and DELETE automatically?
No. With no conditions, both modes generate statements without WHERE. Review destructive output manually.