# Query parameters > A comment at the top of the tab declares a typed parameter and its value. The editor types every @name, the run sends real BigQuery query parameters, and the values travel with the SQL in share links, history and git. Source: https://docs.querylab.io/writing-sql/query-parameters/ **Query 1** ```sql -- querylab:param start_date DATE = '2025-09-01' -- querylab:param makes ARRAY = ['FORD', 'VOLKSWAGEN'] SELECT make, COUNT(*) AS tests FROM `ace-analytics.warehouse.fact_mot_test` WHERE completed_date >= @start_date AND make IN UNNEST(@makes) GROUP BY make ``` ## Declaring a parameter One line per parameter, anywhere in the tab: ```sql -- querylab:param = ``` - The type is any BigQuery parameter type, including `ARRAY<…>`, `STRUCT<…>`, `RANGE<…>` and `INTERVAL`. Type parameters are the one thing that isn't allowed: a query parameter is a `STRING`, never a `STRING(10)`. - The value is a GoogleSQL literal, checked against the type as you type. `NULL` is a valid value for any type, and `[]` is a real empty array. - Spacing and case of `querylab:param` don't matter, and parameter names match the query case-insensitively, as they do in BigQuery. Declaring the same name twice is an error on the second line. Typing `-- ` opens completions for `querylab:`, then the directive name. After `@` in the query, completions offer the declared names; after `@person.`, the fields of a `STRUCT` parameter. ## What the editor knows A declared `@name` has the declared type everywhere Prism looks at the query: hover shows it, type checks use it, and a comparison against the wrong type is flagged like any other type mismatch. An `@name` that no directive declares is an error, and the run is blocked with the same message: ```sql SELECT make FROM `ace-analytics.warehouse.fact_mot_test` WHERE completed_date >= @start_date ``` Parameter @start_date has no value. Declare it with -- querylab:param start_date = . The quick fix **Declare parameter @start_date** inserts the directive at the top of the tab. The type is prefilled when the query gives it away (`completed_date >= @start_date` makes it a `DATE`, `IN UNNEST(@makes)` an array, `LIMIT @n` an `INT64`); otherwise it is `STRING = ''`. > **The editor is the only early warning.** BigQuery's dry run does not check parameters: a query that reads `@x` with no value estimates fine and fails only when it runs. The editor's error is what stands between you and that failed run. ## What happens at run time Every run and every cost estimate sends the declared parameters as typed BigQuery query parameters (`parameterMode: NAMED`, with `arrayType`, `structTypes` and nested values where the type needs them). The directive lines stay in the SQL as comments, which BigQuery ignores. Running a selection keeps the parameters: the directives the selection references are carried along, so a highlighted fragment runs like the whole tab. Because the values live in the SQL, nothing is hidden in tab state. A share link, a history entry, a pasted snippet or a committed file carries its parameters with it. ## Editing values from the status bar Click the ⚙ at the left of the status bar. The **Parameters** section lists each declared parameter with its type, its line, and an editable value. Editing a value rewrites just that part of the directive line, as its own undo step. A value that comes from a Jinja template can't be edited here; the panel says so and points you at the SQL. ## Why not DECLARE? `DECLARE start_date DATE DEFAULT '2025-09-01';` works, but it turns the query into a multi-statement script, and that costs you three things: - A script's results never come from BigQuery's result cache, so repeat runs are billed again. - The estimate for a script is a lower bound, not the price of the run. - The SQL can't be pasted into code that binds parameters; `@start_date` can, unchanged. A parameter directive keeps the query a single statement and matches what your application sends in production. ## Parameters in notebooks A SQL cell can declare parameters with the same directive lines, and they flow to the cells below it, like notebook variables do. A cell lower in the notebook can redeclare a name to override the value from above. Plain assignments (`name = 'x';`) keep working and feed `@name` as a `STRING`; the editor offers **Convert to typed parameter** on assignments used that way.