diff --git a/frontend/src/metabase/dashboard/dashboard.js b/frontend/src/metabase/dashboard/dashboard.js
index c9a6eeb9fe2caf7171ecc7407658326f9a64bcbb..9ead04cd94b1261103381693f31e2ab35d9c5a5d 100644
--- a/frontend/src/metabase/dashboard/dashboard.js
+++ b/frontend/src/metabase/dashboard/dashboard.js
@@ -8,7 +8,7 @@ import { normalize, Schema, arrayOf } from "normalizr";
 
 import MetabaseAnalytics from "metabase/lib/analytics";
 import { getPositionForNewDashCard } from "metabase/lib/dashboard_grid";
-
+import { applyParameters } from "metabase/meta/Card";
 import { fetchDatabaseMetadata } from "metabase/redux/metadata";
 
 const DATASET_SLOW_TIMEOUT   = 15 * 1000;
@@ -136,24 +136,7 @@ export const fetchCardData = createThunkAction(FETCH_CARD_DATA, function(card, d
 
         let dashboard = dashboards[dashboardId];
 
-        let parameters = [];
-        if (dashboard && dashboard.parameters) {
-            for (const parameter of dashboard.parameters) {
-                let mapping = _.findWhere(dashcard && dashcard.parameter_mappings, { card_id: card.id, parameter_id: parameter.id });
-                if (parameterValues[parameter.id] != null) {
-                    parameters.push({
-                        type: parameter.type,
-                        target: mapping && mapping.target,
-                        value: parameterValues[parameter.id]
-                    });
-                }
-            }
-        }
-
-        let datasetQuery = {
-            ...card.dataset_query,
-            parameters
-        };
+        const datasetQuery = applyParameters(card, dashboard.parameters, parameterValues, dashcard && dashcard.parameter_mappings);
 
         let slowCardTimer = setTimeout(() => {
             if (result === null) {
diff --git a/frontend/src/metabase/meta/Card.js b/frontend/src/metabase/meta/Card.js
index 62d4ac56146e03e247873a91eb6b8902d067608b..36df109e35c1cc70f41d250fe3cd9f56ccec7f8e 100644
--- a/frontend/src/metabase/meta/Card.js
+++ b/frontend/src/metabase/meta/Card.js
@@ -1,13 +1,16 @@
 /* @flow */
 
 import type { StructuredQueryObject, NativeQueryObject, TemplateTag } from "./types/Query";
-import type { CardObject, StructuredDatasetQueryObject, NativeDatasetQueryObject } from "./types/Card";
+import type { CardObject, DatasetQueryObject, StructuredDatasetQueryObject, NativeDatasetQueryObject } from "./types/Card";
+import type { ParameterObject, ParameterId, ParameterMappingObject, ParameterMappingTarget } from "metabase/meta/types/Dashboard";
 
 declare class Object {
     static values<T>(object: { [key:string]: T }): Array<T>;
 }
 
 import * as Query from "./Query";
+import QueryLib from "metabase/lib/query";
+import _ from "underscore";
 
 export const STRUCTURED_QUERY_TEMPLATE: StructuredDatasetQueryObject = {
     type: "query",
@@ -62,3 +65,46 @@ export function getTemplateTags(card: ?CardObject): Array<TemplateTag> {
         Object.values(card.dataset_query.native.template_tags) :
         [];
 }
+
+export function applyParameters(
+    card: CardObject,
+    parameters: Array<ParameterObject>,
+    parameterValues: { [key: ParameterId]: string } = {},
+    parameterMappings: Array<ParameterMappingObject> = []
+): DatasetQueryObject {
+    const datasetQuery = JSON.parse(JSON.stringify(card.dataset_query));
+    // clean the query
+    if (datasetQuery.type === "query") {
+        datasetQuery.query = QueryLib.cleanQuery(datasetQuery.query);
+    }
+    datasetQuery.parameters = [];
+    for (const parameter of parameters || []) {
+        let value = parameterValues[parameter.id];
+
+        // dashboards
+        const mapping = _.findWhere(parameterMappings, { card_id: card.id, parameter_id: parameter.id });
+        if (value != null && mapping) {
+            datasetQuery.parameters.push({
+                type: parameter.type,
+                target: mapping.target,
+                value: value
+            });
+        }
+
+        // SQL parameters
+        if (datasetQuery.type === "native") {
+            let tag = _.findWhere(datasetQuery.native.template_tags, { id: parameter.id });
+            if (tag) {
+                datasetQuery.parameters.push({
+                    type: parameter.type,
+                    target: tag.type === "dimension" ?
+                        ["dimension", ["template-tag", tag.name]]:
+                        ["variable", ["template-tag", tag.name]],
+                    value: value
+                });
+            }
+        }
+    }
+
+    return datasetQuery;
+}
diff --git a/frontend/src/metabase/meta/types/Card.js b/frontend/src/metabase/meta/types/Card.js
index 94859cc4a6ec1b22429dea7a6a6670f41978a35e..c890081a825c355813a13abd75d572abc10eb30a 100644
--- a/frontend/src/metabase/meta/types/Card.js
+++ b/frontend/src/metabase/meta/types/Card.js
@@ -2,6 +2,7 @@
 
 import type { DatabaseId } from "./base";
 import type { StructuredQueryObject, NativeQueryObject } from "./Query";
+import type { ParameterInstance } from "./Dashboard";
 
 export type CardId = number;
 
@@ -17,13 +18,15 @@ export type CardObject = {
 export type StructuredDatasetQueryObject = {
     type: "query",
     database: ?DatabaseId,
-    query: StructuredQueryObject
+    query: StructuredQueryObject,
+    parameters?: Array<ParameterInstance>
 };
 
 export type NativeDatasetQueryObject = {
     type: "native",
     database: ?DatabaseId,
     native: NativeQueryObject,
+    parameters?: Array<ParameterInstance>
 };
 
 export type DatasetQueryObject = StructuredDatasetQueryObject | NativeDatasetQueryObject;
diff --git a/frontend/src/metabase/meta/types/Dashboard.js b/frontend/src/metabase/meta/types/Dashboard.js
index e1c3b5bdc2c41f97f4e7b383376219ff801c370b..187f14098910d6e332b933ac59330bf98c9447d1 100644
--- a/frontend/src/metabase/meta/types/Dashboard.js
+++ b/frontend/src/metabase/meta/types/Dashboard.js
@@ -52,3 +52,9 @@ export type ParameterOption = {
     description?: string,
     type: ParameterType
 };
+
+export type ParameterInstance = {
+    type: ParameterType,
+    target: ParameterMappingTarget,
+    value: string
+};
diff --git a/frontend/src/metabase/query_builder/actions.js b/frontend/src/metabase/query_builder/actions.js
index 5a1e5c8fa253be94ff0639ae03be9f836b1794ea..abdd2ca351f2d921fc0cbf80678404eeafb433c5 100644
--- a/frontend/src/metabase/query_builder/actions.js
+++ b/frontend/src/metabase/query_builder/actions.js
@@ -14,6 +14,7 @@ import Query from "metabase/lib/query";
 import { createQuery } from "metabase/lib/query";
 import { loadTableAndForeignKeys } from "metabase/lib/table";
 import Utils from "metabase/lib/utils";
+import { applyParameters } from "metabase/meta/Card";
 
 import { getParameters } from "./selectors";
 
@@ -310,19 +311,10 @@ export const updateTemplateTag = createThunkAction(UPDATE_TEMPLATE_TAG, (templat
 });
 
 export const SET_PARAMETER_VALUE = "SET_PARAMETER_VALUE";
-export const setParameterValue = createThunkAction(SET_PARAMETER_VALUE, (parameterId, value) => {
-    return (dispatch, getState) => {
-        let { qb: { parameterValues } } = getState();
-
-        // apply this specific value
-        parameterValues = { ...parameterValues, [parameterId]: value};
-
-        // the return value from our action is still just the id/value of the parameter set
-        return {id: parameterId, value};
-    };
+export const setParameterValue = createAction(SET_PARAMETER_VALUE, (parameterId, value) => {
+    return { id: parameterId, value };
 });
 
-
 export const NOTIFY_CARD_CREATED = "NOTIFY_CARD_CREATED";
 export const notifyCardCreatedFn = createThunkAction(NOTIFY_CARD_CREATED, (card) => {
     return (dispatch, getState) => {
@@ -685,40 +677,24 @@ export const setQuerySort = createThunkAction(SET_QUERY_SORT, (column) => {
     };
 });
 
+
 // runQuery
 export const RUN_QUERY = "RUN_QUERY";
-export const runQuery = createThunkAction(RUN_QUERY, (card, updateUrl=true, paramValues) => {
+export const runQuery = createThunkAction(RUN_QUERY, (card, updateUrl=true, parameterValues) => {
     return async (dispatch, getState) => {
         const state = getState();
         const parameters = getParameters(state);
 
         // if we got a query directly on the action call then use it, otherwise take whatever is in our current state
         card = card || state.qb.card;
-        card = JSON.parse(JSON.stringify(card));
-        let dataset_query = card.dataset_query,
-            cardIsDirty = isCardDirty(card, state.qb.originalCard);
+        parameterValues = parameterValues || state.qb.parameterValues || {};
 
-        if (dataset_query.query) {
-            // TODO: this needs to be immutable
-            dataset_query.query = Query.cleanQuery(dataset_query.query);
-        }
+        const cardIsDirty = isCardDirty(card, state.qb.originalCard);
 
-        // apply any pseudo-parameters, if specified
-        if (parameters && parameters.length > 0) {
-            let templateTags = card.dataset_query.native.template_tags || {};
-            let parameterValues = paramValues || state.qb.parameterValues || {};
-            dataset_query.parameters = parameters.map(parameter => {
-                let tag = _.findWhere(templateTags, { id: parameter.id });
-                let value = parameterValues[parameter.id];
-                if (value != null && tag) {
-                    return {
-                        type: parameter.type,
-                        target: [tag.type === "dimension" ? "dimension" : "variable", ["template-tag", tag.name]],
-                        value: value
-                    };
-                }
-            }).filter(p => p);
-        }
+        card = {
+            ...card,
+            dataset_query: applyParameters(card, parameters, parameterValues)
+        };
 
         if (updateUrl) {
             state.qb.updateUrl(card, cardIsDirty);
@@ -728,14 +704,14 @@ export const runQuery = createThunkAction(RUN_QUERY, (card, updateUrl=true, para
         let startTime = new Date();
 
         // make our api call
-        Metabase.dataset({ timeout: cancelQueryDeferred.promise }, dataset_query, function (queryResult) {
+        Metabase.dataset({ timeout: cancelQueryDeferred.promise }, card.dataset_query, function (queryResult) {
             dispatch(queryCompleted(card, queryResult));
 
         }, function (error) {
             dispatch(queryErrored(startTime, error));
         });
 
-        MetabaseAnalytics.trackEvent("QueryBuilder", "Run Query", dataset_query.type);
+        MetabaseAnalytics.trackEvent("QueryBuilder", "Run Query", card.dataset_query.type);
 
         // HACK: prevent SQL editor from losing focus
         try { ace.edit("id_sql").focus() } catch (e) {}