One of the main functions of the package is to support parameterization of SQL. Often, small variations of SQL need to be generated based on some parameters. SqlRender offers a simple markup syntax inside the SQL code to allow parameterization. Rendering the SQL based on parameter values is done using the renderSql()
function.
The @
character can be used to indicate parameter names that need to be exchange for actual parameter values when rendering. In the following example, a variable called a
is mentioned in the SQL. In the call to the renderSql function the value of this parameter is defined:
sql <- "SELECT * FROM table WHERE id = @a;"
+renderSql(sql, a = 123)$sql
#> [1] "SELECT * FROM table WHERE id = 123;"
+Note that, unlike the parameterization offered by most database management systems, it is just as easy to parameterize table or field names as values:
+sql <- "SELECT * FROM @x WHERE id = @a;"
+renderSql(sql, x = "my_table", a = 123)$sql
#> [1] "SELECT * FROM my_table WHERE id = 123;"
+The parameter values can be numbers, strings, booleans, as well as vectors, which are converted to comma-delimited lists:
+sql <- "SELECT * FROM table WHERE id IN (@a);"
+renderSql(sql, a = c(1, 2, 3))$sql
#> [1] "SELECT * FROM table WHERE id IN (1,2,3);"
+For some or all parameters, it might make sense to define default values that will be used unless the user specifies another value. This can be done using the {DEFAULT @parameter = value}
syntax:
sql <- "{DEFAULT @a = 1} SELECT * FROM table WHERE id = @a;"
+renderSql(sql)$sql
#> [1] "SELECT * FROM table WHERE id = 1;"
+renderSql(sql, a = 2)$sql
#> [1] "SELECT * FROM table WHERE id = 2;"
+Defaults for multiple variables can be defined:
+sql <- "{DEFAULT @a = 1} {DEFAULT @x = 'my_table'} SELECT * FROM @x WHERE id = @a;"
+renderSql(sql)$sql
#> [1] "SELECT * FROM my_table WHERE id = 1;"
+Sometimes blocks of codes need to be turned on or off based on the values of one or more parameters. This is done using the {Condition} ? {if true} : {if false}
syntax. If the condition evaluates to true or 1, the if true block is used, else the if false block is shown (if present).
sql <- "SELECT * FROM table {@x} ? {WHERE id = 1}"
+renderSql(sql, x = FALSE)$sql
#> [1] "SELECT * FROM table "
+renderSql(sql, x = TRUE)$sql
#> [1] "SELECT * FROM table WHERE id = 1"
+Simple comparisons are also supported:
+sql <- "SELECT * FROM table {@x == 1} ? {WHERE id = 1};"
+renderSql(sql, x = 1)$sql
#> [1] "SELECT * FROM table WHERE id = 1;"
+renderSql(sql, x = 2)$sql
#> [1] "SELECT * FROM table ;"
+As well as the IN
operator:
sql <- "SELECT * FROM table {@x IN (1,2,3)} ? {WHERE id = 1}; "
+renderSql(sql, x = 2)$sql
#> [1] "SELECT * FROM table WHERE id = 1; "
+SQL for one platform (e.g. Microsoft SQL Server) will not always execute on other platforms (e.g. Oracle). The translateSql()
function can be used to translate between different dialects, but there are some limitations.
A first limitation is that the starting dialect has to be SQL Server. The reason for this is that this dialect is in general the most specific. For example, the number of days between two dates in SQL Server has to be computed using the DATEDIFF function: DATEDIFF(dd,a,b)
. In other languages one can simply subtract the two dates: b-a
. Since you’d need to know a and b are dates, it is not possible to go from other languages to SQL Server, only the other way around.
A second limitation is that currently only these dialects are supported as targets: Oracle, PostgreSQL, Microsoft PDW (Parallel Data Warehouse), Impala, Netezza, Google BigQuery, and Amazon Redhift.
+A third limitation is that only a limited set of translation rules have currently been implemented, although adding them to the list should not be hard.
+A last limitation is that not all functions supported in one dialect have an equivalent in other dialects.
+Below an example:
+sql <- "SELECT DATEDIFF(dd,a,b) FROM table; "
+translateSql(sql, targetDialect = "oracle")$sql
#> [1] "SELECT (CAST(b AS DATE) - CAST(a AS DATE)) FROM table ; "
+The targetDialect
parameter can have the following values:
These SQL Server functions have been tested and were found to be translated correctly to the various dialects:
+Function | +Function | +Function | +Function | +
---|---|---|---|
ABS | +DATEDIFF | +LOG | +ROUND | +
ACOS | +DATEFROMPARTS | +LOG10 | +ROW_NUMBER | +
ASIN | +DATETIMEFROMPARTS | +LOWER | +RTRIM | +
ATAN | +DAY | +LTRIM | +SIN | +
AVG | +EOMONTH | +MAX | +SQRT | +
CAST | +EXP | +MIN | +SQUARE | +
CEILING | +FLOOR | +MONTH | +STDEV | +
CHARINDEX | +GETDATE | +NEWID | +SUM | +
CONCAT | +HASHBYTES* | +PI | +TAN | +
COS | +ISNULL | +POWER | +UPPER | +
COUNT | +ISNUMERIC | +RAND | +VAR | +
COUNT_BIG | +LEFT | +RANK | +YEAR | +
DATEADD | +LEN | +RIGHT | ++ |
Similarly, many SQL syntax structures are supported. Here is a non-exhaustive lists of things that we know will translate well:
+SELECT * FROM table; -- Simple selects
+
+SELECT * FROM table_1 INNER JOIN table_2 ON a = b; -- Selects with joins
+
+SELECT * FROM (SELECT * FROM table_1) tmp WHERE a = b; -- Nested queries
+
+SELECT TOP 10 * FROM table; -- Limiting to top rows
+
+SELECT * INTO new_table FROM table; -- Selecting into a new table
+
+CREATE TABLE table (field INT); -- Creating tables
+
+INSERT INTO other_table (field_1) VALUES (1); -- Inserting verbatim values
+
+INSERT INTO other_table (field_1) SELECT value FROM table; -- Inserting from SELECT
+
+DROP TABLE table; -- Simple drop commands
+
+IF OBJECT_ID('ACHILLES_analysis', 'U') IS NOT NULL -- Drop table if it exists
+ DROP TABLE ACHILLES_analysis;
+
+IF OBJECT_ID('tempdb..#cohorts', 'U') IS NOT NULL -- Drop temp table if it exists
+ DROP TABLE #cohorts;
+
+WITH cte AS (SELECT * FROM table) SELECT * FROM cte; -- Common table expressions
+
+SELECT ROW_NUMBER() OVER (PARTITION BY a ORDER BY b) -- OVER clauses
+ AS "Row Number" FROM table;
+
+SELECT CASE WHEN a=1 THEN a ELSE 0 END AS value FROM table; -- CASE WHEN clauses
+
+SELECT * FROM a UNION SELECT * FROM b -- UNIONs
+
+SELECT * FROM a INTERSECT SELECT * FROM b -- INTERSECTIONs
+
+SELECT * FROM a EXCEPT SELECT * FROM b -- EXCEPT
String concatenation is one area where SQL Server is less specific than other dialects. In SQL Server, one would write SELECT first_name + ' ' + last_name AS full_name FROM table
, but this should be SELECT first_name || ' ' || last_name AS full_name FROM table
in PostgreSQL and Oracle. SqlRender tries to guess when values that are being concatenated are strings. In the example above, because we have an explicit string (the space surrounded by single quotation marks), the translation will be correct. However, if the query had been SELECT first_name + last_name AS full_name FROM table
, SqlRender would have had no clue the two fields were strings, and would incorrectly leave the plus sign. Another clue that a value is a string is an explicit cast to VARCHAR, so SELECT last_name + CAST(age AS VARCHAR(3)) AS full_name FROM table
would also be translated correctly. To avoid ambiguity altogether, it is probable best to use the CONCAT()
function to concatenate two or more strings.
Temp tables can be very useful to store intermediate results, and when used correctly can be used to dramatically improve performance of queries. In Postgres, PDW, RedShift and SQL Server temp tables have very nice properties: they’re only visible to the current user, are automatically dropped when the session ends, and can be created even when the user has no write access. Unfortunately, in Oracle temp tables are basically permanent tables, with the only difference that the data inside the table is only visible to the current user. This is why, in Oracle, SqlRender will try to emulate temp tables by
+For example:
+sql <- "SELECT * FROM #children;"
+translateSql(sql, targetDialect = "oracle", oracleTempSchema = "temp_schema")$sql
#> [1] "SELECT * FROM temp_schema.kp8f8g5fchildren ;"
+Note that the user will need to have write privileges on temp_schema
.
Also note that because Oracle has a limit on table names of 30 characters, temp table names are only allowed to be at most 22 characters long because else the name will become too long after appending the session ID.
+Futhermore, remember that temp tables are not automatically dropped on Oracle, so you will need to explicitly TRUNCATE
and DROP
all temp tables once you’re done with them to prevent orphan tables accumulating in the Oracle temp schema.
If possible, try to avoid using temp tables altogether. Sometimes one could use Common Table Expressions (CTEs) when one would normally use a temp table. For example, instead of
+SELECT * INTO #children FROM person WHERE year_of_birth > 2000;
+SELECT * FROM #children WHERE gender = 8507;
you could use
+WITH children AS (SELECT * FROM person WHERE year_of_birth > 2000)
+SELECT * FROM children WHERE gender = 8507;
One of the few points where SQL Server is less explicit than other dialects is that it allows implicit casts. For example, this code will work on SQL Server:
+CREATE TABLE #temp (txt VARCHAR);
+
+INSERT INTO #temp
+SELECT '1';
+
+SELECT * FROM #temp WHERE txt = 1;
Even though txt
is a VARCHAR field and we are comparing it with an integer, SQL Server will automatically cast one of the two to the correct type to allow the comparison. In contrast, other dialects such as PosgreSQL will throw an error when trying to compare a VARCHAR with an INT.
You should therefore always make casts explicit. In the above example, the last statement should be replaced with either
+SELECT * FROM #temp WHERE txt = CAST(1 AS VARCHAR);
or
+SELECT * FROM #temp WHERE CAST(txt AS INT) = 1;
Some DBMS platforms such as SQL Server always perform string comparisons in a case-insensitive way, while others such as PostgreSQL are always case sensitive. It is therefore recommended to always assume case-sensitive comparisons, and to explicitly make comparisons case-insensitive when unsure about the case. For example, instead of
+SELECT * FROM concept WHERE concep_class_id = 'Clinical Finding'
it is preferred to use
+SELECT * FROM concept WHERE LOWER(concep_class_id) = 'clinical finding'
In SQL Server, tables are located in a schema, and schemas reside in a database. For example, cdm_data.dbo.person
refers to the person
table in the dbo
schema in the cdm_data
database. In other dialects, even though a similar hierarchy often exists they are used very differently. In SQL Server, there is typically one schema per database (often called dbo
), and users can easily use data in different databases. On other platforms, for example in PostgreSQL, it is not possible to use data across databases in a single session, but there are often many schemas in a database. In PostgreSQL one could say that the equivalent of SQL Server’s database is the schema.
We therefore recommend concatenating SQL Server’s database and schema into a single parameter, which we typically call @databaseSchema
. For example, we could have the parameterized SQL
SELECT * FROM @databaseSchema.person
where on SQL Server we can include both database and schema names in the value: databaseSchema = "cdm_data.dbo"
. On other platforms, we can use the same code, but now only specify the schema as the parameter value: databaseSchema = "cdm_data"
.
The one situation where this will fail is the USE
command, since USE cdm_data.dbo;
will throw an error. It is therefore preferred not to use the USE
command, but always specify the database / schema where a table is located. However, if one wanted to use it anyway, we recommend creating two variables, one called @database
and the other called @databaseSchema
. For example, for this parameterized SQL:
SELECT * FROM @databaseSchema.person;
+USE @database;
+SELECT * FROM person
we can set database = "cdm_data"
and the other called databaseSchema = "cdm_data.dbo"
. On platforms other than SQL Server, the two variables will hold the same value and only on SQL Server will they be different. Within an R function, it is even possible to derive one variable from the other, so the user of your function would need to specify only one value:
foo <- function(databaseSchema, dbms) {
+ database <- strsplit(databaseSchema, "\\.")[[1]][1]
+ sql <- "SELECT * FROM @databaseSchema.person; USE @database; SELECT * FROM person;"
+ sql <- renderSql(sql, databaseSchema = databaseSchema, database = database)$sql
+ sql <- translateSql(sql, targetDialect = dbms)$sql
+ return(sql)
+}
+foo("cdm_data.dbo", "sql server")
#> [1] "SELECT * FROM cdm_data.dbo.person; USE cdm_data; SELECT * FROM person;"
+foo("cdm_data", "postgresql")
#> [1] "SELECT * FROM cdm_data.person; SET search_path TO cdm_data; SELECT * FROM person;"
+Both PDW and RedShift are massively parallel processing platforms, meaning they consist of many nodes that work together. In such an environment, significant increases in performance can be achieved by finetuning the SQL for these platforms. Probably most importantly, developers can specify the way data is distributed over the nodes. Ideally, data in a node only needs to be combined with data in the same node. For example, if I have two tables with the field person_id
, I would like all records with the same person ID to be on the same node, so a join on person_id
can be performed locally without exchanging data between nodes.
SQL Server SQL, our source dialect, does not allow for these optimizations, so we’ve introduced the notion of hints. In the following example, a hint is provided on which field should be used for the distribution of data across nodes:
+--HINT DISTRIBUTE_ON_KEY(person_id)
+SELECT * INTO one_table FROM other_table;
which will translate into the following on PDW:
+--HINT DISTRIBUTE_ON_KEY(person_id)
+IF XACT_STATE() = 1 COMMIT;
+CREATE TABLE one_table WITH (DISTRIBUTION = HASH(person_id)) AS
+SELECT * FROM other_table;
Another tuning parameter is the key to sort a table on. This can be also be specified in a hint:
+--HINT SORT_ON_KEY(INTERLEAVED:start_date)
+CREATE TABLE cdm.my_table (row_id INT, start_date);
translates to the following on RedShift:
+--HINT SORT_ON_KEY(INTERLEAVED:start_date)
+CREATE TABLE cdm.my_table (row_id INT, start_date)
+INTERLEAVED SORTKEY(start_date);
The hints should be formatted exactly as shown above, and directly precede the statement where the table is created.
+Debugging parameterized SQL can be a bit complicated; Only the rendered SQL can be tested against a database server, but changes to the code should be made in the parameterized (pre-rendered) SQL.
+A Shiny app is included in the SqlRender package for interactively editing source SQL and generating rendered and translated SQL. The app can be started using:
+ +Which will open the default browser with the app.
+In addition, two functions have been developed to aid the debugging process: renderSqlFile()
and translateSqlFile()
. These can be used to read SQL from file, render or translate it, and write it back to file. For example:
translateSqlFile("parameterizedSql.txt", "renderedSql.txt")
will render the file, using the default parameter values specified in the SQL. What works well for us is editing in the parameterized file, (re)running the command above, and have the rendered SQL file open in a SQL client for testing. Any problems reported by the server can be dealt with in the source SQL, and can quickly be re-rendered.
+Often, the SQL code will become part of an R package, where it might be used to perform initial data-preprocessing and extraction before further analysis. We’ve developed the following practice for doing so: The parameterized SQL should be located in the inst/sql/ folder of the package. The parameterized SQL for SQL Server should be in the inst/sql/sql_server/ folder. If for some reason you do not want to use the translation functions to generate the SQL for some dialect (e.g because dialect specific code might be written that gives better performance), a dialect-specific version of the parameterized SQL should be placed in a folder with the name of that dialect, for example inst/sql/oracle/. SqlRender has a function loadRenderTranslateSql()
that will first check if a dialect-specific version is available for the target dialect. If it is, that version will be rendered, else the SQL Server version will be rendered and subsequently translated to the target dialect.
The createRWrapperForSql()
function can be used to create an R wrapper around a rendered SQL file, using the loadRenderTranslateSql()
function . For example, suppose we have a text file called test.sql containing the following parameterized SQL:
{DEFAULT @selected_value = 1}
+SELECT * FROM table INTO result where x = @selected_value;
Then the command
+createRWrapperForSql(sqlFilename = "test.sql",
+ rFilename = "test.R",
+ packageName = "myPackage")
would result in the file test.R being generated containing this R code:
+#' Todo: add title
+#'
+#' @description
+#' Todo: add description
+#'
+#' @details
+#' Todo: add details
+#'
+#' @param connectionDetails An R object of type \code{ConnectionDetails} created ...
+#' @param selectedValue
+#'
+#' @export
+test <- function(connectionDetails, selectedValue = 1) {
+ renderedSql <- loadRenderTranslateSql("test.txt", packageName = "myPackage",
+ dbms = connectionDetails$dbms, selected_value = selectedValue)
+ conn <- connect(connectionDetails)
+
+ writeLines("Executing multiple queries. This could take a while")
+ executeSql(conn, renderedSql)
+ writeLines("Done")
+
+ dummy <- dbDisconnect(conn)
+}
This code expects the file test.sql to be located in the inst/sql/sql_server/ folder of the package source.
+Note that the parameters are identified by the declaration of default values, and that snake_case names (our standard for SQL) are converted to camelCase names (our standard for R).
+This is an R package for rendering parameterized SQL, and translating it to different SQL dialects. SqlRender can also be used as a stand-alone Java library and a command-line executable.
+This exampe shows the use of parameters, as well as SqlRender’s {if} ? {then} : {else} syntax:
+sql <- renderSql("SELECT * FROM @a; {@b != ''}?{USE @b;}", a = "my_table", b = "my_schema")$sql
will produce the variable sql
containing this value:
"SELECT * FROM my_table; USE my_schema;"
+subsequently running this code
+sql <- translateSql(sql, "sql server", "oracle")$sql
will produce the variable sql
containing this value:
"SELECT * FROM my_table; ALTER SESSION SET current_schema = my_schema;"
+The SqlRender package is an R package wrapped around a Java library. The rJava package is used as interface.
+The Java library is available as a JAR file.
+Running the package requires R with the package rJava installed. Also requires Java 1.6 or higher.
+In R, use the following commands to install the latest stable version from CRAN:
+install.packages("SqlRender")
To install the latest development version directly from GitHub, use:
+install.packages("devtools")
+library(devtools)
+install_github("ohdsi/SqlRender")
Once installed, you can try out SqlRender in a Shiny app that comes with the package:
+library(SqlRender)
+launchSqlRenderDeveloper()
You can fetch the JAR file in the inst/java folder of this repository, or use Maven:
+First add the SqlRender repository so that maven can find and download the SqlRender artifact automatically:
+<repositories>
+<repository>
+ <id>ohdsi</id>
+ <name>repo.ohdsi.org</name>
+ <url>http://repo.ohdsi.org:8085/nexus/content/repositories/releases</url>
+</repository>
+<repository>
+ <id>ohdsi.snapshots</id>
+ <name>repo.ohdsi.org-snapshots</name>
+ <url>http://repo.ohdsi.org:8085/nexus/content/repositories/snapshots</url>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+</repository>
+</repositories>
2: Include the SqlRender dependency in your pom.xml
+<dependency>
+<groupId>org.ohdsi.sql</groupId>
+<artifactId>SqlRender</artifactId>
+<version>1.0.0-SNAPSHOT</version>
+</dependency>
Apache License 2.0
+SqlRender
+ + + + +Convert a camel case string to snake case
+ + +camelCaseToSnakeCase(string)+ +
string | +The string to be converted |
+
---|
A string
+ + ++camelCaseToSnakeCase("cdmDatabaseSchema")#> [1] "cdm_database_schema"# > 'cdm_database_schema'
createRWrapperForSql
creates an R wrapper for a parameterized SQL file. The created R script
+file will contain a single function, that executes the SQL, and accepts the same parameters as
+specified in the SQL.
createRWrapperForSql(sqlFilename, rFilename, packageName, + createRoxygenTemplate = TRUE)+ +
sqlFilename | +The SQL file. |
+
---|---|
rFilename | +The name of the R file to be generated. Defaults to the name of the +SQL file with the extention reset to R. |
+
packageName | +The name of the package that will contains the SQL file. |
+
createRoxygenTemplate | +If true, a template of Roxygen comments will be added. |
+
This function reads the declarations of defaults in the parameterized SQL file, and creates an R
+function that exposes the parameters. It uses the loadRenderTranslateSql
function, and
+assumes the SQL will be used inside a package. To use inside a package, the SQL file should be
+placed in the inst/sql/sql_server folder of the package.
+not_run({ + # This will create a file called CohortMethod.R: + createRWrapperForSql("CohortMethod.sql", packageName = "CohortMethod") +})
Launch the SqlRender Developer Shiny app
+ + +launchSqlRenderDeveloper(launch.browser = TRUE)+ +
launch.browser | +Should the app be launched in your default browser, or in a Shiny window. +Note: copying to clipboard will not work in a Shiny window. |
+
---|
Launches a Shiny app that allows the user to develop SQL and see how it translates to the supported dialects.
+ + +loadRenderTranslateSql
Loads a SQL file contained in a package, renders it and translates it
+to the specified dialect
loadRenderTranslateSql(sqlFilename, packageName, dbms = "sql server", ..., + oracleTempSchema = NULL)+ +
sqlFilename | +The source SQL file |
+
---|---|
packageName | +The name of the package that contains the SQL file |
+
dbms | +The target dialect. Currently 'sql server', 'oracle', 'postgres', and +'redshift' are supported |
+
... | +Parameter values used for |
+
oracleTempSchema | +A schema that can be used to create temp tables in when using Oracle. |
+
Returns a string containing the rendered SQL.
+ +This function looks for a SQL file with the specified name in the inst/sql/<dbms> folder of the
+specified package. If it doesn't find it in that folder, it will try and load the file from the
+inst/sql/sql_server folder and use the translateSql
function to translate it to the
+requested dialect. It will subsequently call the renderSql
function with any of the
+additional specified parameters.
+not_run({ + renderedSql <- loadRenderTranslateSql("CohortMethod.sql", + packageName = "CohortMethod", + dbms = connectionDetails$dbms, + CDM_schema = "cdmSchema") +})
readSql
loads SQL from a file
readSql(sourceFile)+ +
sourceFile | +The source SQL file |
+
---|
Returns a string containing the SQL.
+ +readSql
loads SQL from a file
+not_run({ + readSql("myParamStatement.sql") +})
renderSql
Renders SQL code based on parameterized SQL and parameter values.
renderSql(sql = "", ...)+ +
sql | +The parameterized SQL |
+
---|---|
... | +Parameter values |
+
A list containing the following elements:
The original +parameterized SQL code
The rendered sql
This function takes parameterized SQL and a list of parameter values and renders the SQL that can +be send to the server. Parameterization syntax:
Parameters are +indicated using a @ prefix, and are replaced with the actual values provided in the renderSql +call.
Default values for parameters can be +defined using curly and the DEFAULT keyword.
The if-then-else +pattern is used to turn on or off blocks of SQL code.
+renderSql("SELECT * FROM @a;", a = "myTable")#> $originalSql +#> [1] "SELECT * FROM @a;" +#> +#> $sql +#> [1] "SELECT * FROM myTable;" +#> +#> $parameters +#> $parameters$a +#> [1] "myTable" +#> +#>renderSql("SELECT * FROM @a {@b}?{WHERE x = 1};", a = "myTable", b = "true")#> $originalSql +#> [1] "SELECT * FROM @a {@b}?{WHERE x = 1};" +#> +#> $sql +#> [1] "SELECT * FROM myTable WHERE x = 1;" +#> +#> $parameters +#> $parameters$a +#> [1] "myTable" +#> +#> $parameters$b +#> [1] "true" +#> +#>renderSql("SELECT * FROM @a {@b == ''}?{WHERE x = 1}:{ORDER BY x};", a = "myTable", b = "true")#> $originalSql +#> [1] "SELECT * FROM @a {@b == ''}?{WHERE x = 1}:{ORDER BY x};" +#> +#> $sql +#> [1] "SELECT * FROM myTable ORDER BY x;" +#> +#> $parameters +#> $parameters$a +#> [1] "myTable" +#> +#> $parameters$b +#> [1] "true" +#> +#>renderSql("SELECT * FROM @a {@b != ''}?{WHERE @b = 1};", a = "myTable", b = "y")#> $originalSql +#> [1] "SELECT * FROM @a {@b != ''}?{WHERE @b = 1};" +#> +#> $sql +#> [1] "SELECT * FROM myTable WHERE y = 1;" +#> +#> $parameters +#> $parameters$a +#> [1] "myTable" +#> +#> $parameters$b +#> [1] "y" +#> +#>renderSql("SELECT * FROM @a {1 IN (@c)}?{WHERE @b = 1};", + a = "myTable", + b = "y", + c = c(1, 2, 3, 4))#> $originalSql +#> [1] "SELECT * FROM @a {1 IN (@c)}?{WHERE @b = 1};" +#> +#> $sql +#> [1] "SELECT * FROM myTable WHERE y = 1;" +#> +#> $parameters +#> $parameters$a +#> [1] "myTable" +#> +#> $parameters$b +#> [1] "y" +#> +#> $parameters$c +#> [1] "1,2,3,4" +#> +#>renderSql("{DEFAULT @b = \"someField\"}SELECT * FROM @a {@b != ''}?{WHERE @b = 1};", + a = "myTable")#> $originalSql +#> [1] "{DEFAULT @b = \"someField\"}SELECT * FROM @a {@b != ''}?{WHERE @b = 1};" +#> +#> $sql +#> [1] "SELECT * FROM myTable WHERE someField = 1;" +#> +#> $parameters +#> $parameters$a +#> [1] "myTable" +#> +#>renderSql("SELECT * FROM @a {@a == 'myTable' & @b != 'x'}?{WHERE @b = 1};", + a = "myTable", + b = "y")#> $originalSql +#> [1] "SELECT * FROM @a {@a == 'myTable' & @b != 'x'}?{WHERE @b = 1};" +#> +#> $sql +#> [1] "SELECT * FROM myTable WHERE y = 1;" +#> +#> $parameters +#> $parameters$a +#> [1] "myTable" +#> +#> $parameters$b +#> [1] "y" +#> +#>
renderSqlFile
Renders SQL code in a file based on parameterized SQL and parameter values,
+and writes it to another file.
renderSqlFile(sourceFile, targetFile, ...)+ +
sourceFile | +The source SQL file |
+
---|---|
targetFile | +The target SQL file |
+
... | +Parameter values |
+
This function takes parameterized SQL and a list of parameter values and renders the SQL that can +be send to the server. Parameterization syntax:
Parameters are +indicated using a @ prefix, and are replaced with the actual values provided in the renderSql +call.
Default values for parameters can be +defined using curly and the DEFAULT keyword.
The if-then-else +pattern is used to turn on or off blocks of SQL code.
+not_run({ + renderSqlFile("myParamStatement.sql", "myRenderedStatement.sql", a = "myTable") +})
Convert a snake case string to camel case
+ + +snakeCaseToCamelCase(string)+ +
string | +The string to be converted |
+
---|
A string
+ + ++snakeCaseToCamelCase("cdm_database_schema")#> [1] "cdmDatabaseSchema"# > 'cdmDatabaseSchema'
splitSql
splits a string containing multiple SQL statements into a vector of SQL statements
splitSql(sql)+ +
sql | +The SQL string to split into separate statements |
+
---|
A vector of strings, one for each SQL statement
+ +This function is needed because some DBMSs (like ORACLE) do not accepts multiple SQL statements +being sent as one execution.
+ + ++splitSql("SELECT * INTO a FROM b; USE x; DROP TABLE c;")#> [1] "SELECT * INTO a FROM b" "USE x" "DROP TABLE c"+
translateSql
translates SQL from one dialect to another
translateSql(sql = "", targetDialect, oracleTempSchema = NULL, + sourceDialect)+ +
sql | +The SQL to be translated |
+
---|---|
targetDialect | +The target dialect. Currently "oracle", "postgresql", "pdw", "impala", "netezza", "bigquery", and +"redshift" are supported |
+
oracleTempSchema | +A schema that can be used to create temp tables in when using Oracle or Impala. |
+
sourceDialect | +Deprecated: The source dialect. Currently, only "sql server" for Microsoft SQL Server +is supported |
+
A list containing the following elements:
The original parameterized +SQL code
The translated SQL
This function takes SQL in one dialect and translates it into another. It uses simple pattern +replacement, so its functionality is limited.
+ + ++translateSql("USE my_schema;", targetDialect = "oracle")#> $originalSql +#> [1] "USE my_schema;" +#> +#> $sql +#> [1] "ALTER SESSION SET current_schema = my_schema;" +#>+
This function takes SQL and translates it to a different dialect.
+ + +translateSqlFile(sourceFile, targetFile, sourceDialect, targetDialect, + oracleTempSchema = NULL)+ +
sourceFile | +The source SQL file |
+
---|---|
targetFile | +The target SQL file |
+
sourceDialect | +Deprecated: The source dialect. Currently, only 'sql server' for Microsoft SQL Server +is supported |
+
targetDialect | +The target dialect. Currently 'oracle', 'postgresql', and 'redshift' are +supported |
+
oracleTempSchema | +A schema that can be used to create temp tables in when using Oracle. |
+
This function takes SQL and translates it to a different dialect.
+ + ++not_run({ + translateSqlFile("myRenderedStatement.sql", + "myTranslatedStatement.sql", + targetDialect = "postgresql") +})
writeSql
writes SQL to a file
writeSql(sql, targetFile)+ +
sql | +A string containing the sql |
+
---|---|
targetFile | +The target SQL file |
+
writeSql
writes SQL to a file
+not_run({ + sql <- "SELECT * FROM @table_name" + writeSql(sql, "myParamStatement.sql") +})