I want to run multiple statements on a single execution against SQL Server. I do it with Node.js but I cannot run the same query using next.jdbc
For example, if I run this:
(def db {:jdbcUrl "jdbc:jtds:sqlserver://localhost:1433/TESTDB;user=sa;password=passwd"})
(def ds (jdbc/get-datasource db))
(jdbc/execute! ds ["select * from EMPLOYEE;select FIRST_NAME from EMPLOYEE;"])
I have also tried to wrap the statement within a transaction with the same result
(jdbc/execute! ds ["BEGIN TRANSACTION select * from EMPLOYEE;select FIRST_NAME from EMPLOYEE; COMMIT"])
I always get the first query.
I have tried Microsoft's JDBC driver also.
Sean Corfield says that if the database supports it, then next.jdbc should support it. next-jdbc: execute multiple statements?
But I cannot make it work
Solution
As indicated by Sean Corfield
(jdbc/execute! ds ["BEGIN select * from EMPLOYEE;select FIRST_NAME from EMPLOYEE; END"] {:multi-rs true})
Yes, you can run multiple statements and multiple result sets back but you have to tell next.jdbc that's the behavior you want.
Take a look at the tests for MS SQL Server running multiple statements: https://github.com/seancorfield/next-jdbc/blob/develop/test/next/jdbc_test.clj#L560-L572
This is mentioned (briefly) in the Getting Started guide: "If you pass the :multi-rs true option to execute!, you will get back a vector of results sets, instead of just one result set: a vector of zero or more vectors."
As far as I know, SQL Server's JDBC driver does not support multiple statements. But, even if it did, you should probably not be using it, as it opens a potential security hole for injection type attacks. Instead, if you really need to execute multiple SQL statements, either refactor your current SQL into a single statement, or else use multiple statements wrapped in a single transaction.
For reference, some other JDBC drivers, such as MySQL, might support multiple statements.
Related
Hello for demonstration purposes I trimmed out my actual sql query.
I have a SQL query
SELECT *
FROM dbdev.training.courses
where dbdev is my DEV database table name. When I migrate to TEST env, I want my query to dynamically change to
SELECT *
FROM dbtest.training.courses
I tried using input parameters like {env: p('db_name')} and using in the query as
SELECT * FROM :env.training.courses
or
SELECT * FROM (:env).training.courses
but none of them worked. I don't want my SQL query in properties file.
Can you please suggest a way to write my SQL query dynamically based on environment?
The only alternative way is to deploy separate jars for different environments with different code.
You can set the value of the property to a variable and then use the variable with string interpolation.
Warning: creating dynamic SQL queries using any kind of string manipulation may expose your application to SQL injection security vulnerabilities.
Example:
#['SELECT * FROM $(vars.database default "dbtest").training.courses']
Actually, you can do a completely dynamic or partially dynamic query using the MuleSoft DB connector.
Please see this repo:
https://github.com/TheComputerClassroom/dynamicSQLGETandPATCH
Also, I'm about to post an update that allows joins.
At a high level, this is a "Query Builder" where the code that builds the query is written in DataWeave 2. I'm working on another version that allows joins between entities, too.
If you have questions, feel free to reply.
One way to do it is :
Create a variable before DB Connector:
getTableName - ${env}.training.courses
Write SQL Query :
Select * from $(getTableName);
Sometimes it works anyway if I forget the ;. But sometimes it doesn't.
And in JDBC and Android SQLite, it seems that I don't need ; at all. I am confused.
When should I use a semicolon?
semicolon indicates end of a statement, so if there are multiple statements then you should use semicolon else it will work fine.
I generally use semicolon as a practice, it can be useful even when you are running queries on sql client e.g. in Sql Developer using semicolon is very helpful if you have multiple statements on worksheet, as you can simply go to that particular statement and use F9 to execute that, without semicolon this is not possible.
It is not mandatory if you run a single query at time, it comes necessary instead if you want to run multiple query with a single command.
However in most of JDBC drivers out there it is not possible to add multiple query separated with semicolon in a single JDBC Command, it exist however the addBatch method that allow you to add multiple statements :
java.sql.Statement stmt=con.createStatement();
stmt.addBatch(insert_query1); //insert_query1
stmt.addBatch(insert_query2); //insert_query2
As a rule of thumb, in JDBC semicolon is not necessary at all, if you need multiple statement use addBatch.
Usually the semicolon is not part of the actual syntax of a statement (as most database internal APIs execute a single statement at a time). Instead the semicolon is an 'end-of-statement' marker or statement separator that is - usually - defined in CLI or scripting tools for the database. This allows that tool to know when a statement ends, so it can send that single statement to the database for execution.
On the other hand, the JDBC API is intended to execute a single(!) statement at a time, therefore you don't need such a separator (the statement is the whole string). This means that a semicolon is not needed, and as it is not part of the actual statement syntax for a lot of database it is also a syntax error to include it. Some JDBC drivers will strip the last ; from a statement to 'fix' that, some drivers don't.
Some drivers allow - contrary to the JDBC specification - multiple statements to be executed as a single string, this usually has to be enabled with a connection property, for example for MySQL it is the option allowMultiQueries (see the MySQL properties for details).
Depends on the DBMS and version number. Semicolons are often optional at the end of a single statement. But if you are going to execute a script with more than one statement, they need to be terminated by a semicolon.
Except maybe the last one. But it seems bad form to be inconsistent.
My project is in Visual Foxpro and I use MS SQL server 2008. When I fire sql queries in batch, some of the queries don't execute. However, no error is thrown. I haven't used BEGIN TRAN and ROLLBACK yet. What should be done ??
that all depends... You don't have any sample of your queries posted to give us an indication of possible failure. However, one thing I've had good response with from VFP to SQL is to build into a string (I prefer using TEXT/ENDTEXT for readabilty), then send that entire value to SQL. If there are any "parameter" based values that are from VFP locally, you can use "?" to indicate it will come from a variable to SQL. Then you can batch all in a single vs multiple individual queries...
vfpField = 28
vfpString = 'Smith'
text to lcSqlCmd noshow
select
YT.blah,
YT.blah2
into
#tempSqlResult
from
yourTable YT
where
YT.SomeKey = ?vfpField
select
ost.Xblah,
t.blah,
t.blah2
from
OtherSQLTable ost
join #tempSqlResult t
on ost.Xblah = t.blahKey;
drop table #tempSqlResult;
endtext
nHandle = sqlconnect( "your connection string" )
nAns = sqlexec( nHandle, lcSqlCmd, "LocalVFPCursorName" )
No I don't have error trapping in here, just to show principle and readability. I know the sample query could have easily been done via a join, but if you are working with some pre-aggregations and want to put them into temp work areas like Localized VFP cursors from a query to be used as your next step, this would work via #tempSqlResult as "#" indicates temporary table on SQL for whatever the current connection handle is.
If you want to return MULTIPLE RESULT SETs from a single SQL call, you can do that too, just add another query that doesn't have an "into #tmpSQLblah" context. Then, all instances of those result cursors will be brought back down to VFP based on the "LocalVFPCursorName" prefix. If you are returning 3 result sets, then VFP will have 3 cursors open called
LocalVFPCursorName
LocalVFPCursorName1
LocalVFPCursorName2
and will be based on the sequence of the queries in the SqlExec() call. But if you can provide more on what you ARE trying to do and their samples, we can offer more specific help too.
I am wondering if anyone can explain why I get different results for the same query string between using the ExecuteSQL function in FM versus querying the database through a database browser (I'm using DBVisualizer).
Specifically, if I run
SELECT COUNT(DISTINCT IMV_ItemID) FROM IMV
in DBVis, I get 2802. In FileMaker, if I evaluate the expression
ExecuteSQL ( "SELECT COUNT(DISTINCT IMV_ItemID) FROM IMV"; ""; "")
then I get 2898. This makes me distrust the ExecuteSQL function. Inside of FM, the IMV table is an ODBC shadow, connected to the central MSSQL database. In DBVis, the application connects via JDBC. However, I don't think that should make any difference.
Any ideas why I get a different count for each method?
Actually, it turns out that when FM executes the SQL, it factors in whitespace, whereas DBVisualizer (not sure about other database browser apps, but I would assume it's the same) do not. Also, since the TRIM() function isn't supported by MSSQL (from what I've seen, at least) it is necessary to make the query inside of the ExecuteSQL statement something like:
SELECT COUNT(DISTINCT(LTRIM(RTRIM(IMV_ItemID)))) FROM IMV
Weird, but it works!
FM keeps a cache of the shadow table's records (for internal field-id-mapping). I'm not sure if the ExecuteSQL() function causes a re-creation of the cache. In other words: maybe the ESS shadow table is out of sync. Try to delete the cache by closing and restarting the FM client or perform a native find first.
You can also try a re-connect to the database server via the Open File script step.
HTH
I am using SQL Server database and after calling a simple SQL script I would like to know how many records were affected by last (or only) executed statement in a script.
I cannot find the reference how to achieve this in Delphi's TADOCommand and I know SQL Server gives this information to provider. I am aware of workarounds like getting ##ROWCOUNT in another query, yet this gives some overhead and unnecessary complexity.
Thanks.
Do you use the
function Execute(var RecordsAffected: Integer; const Parameters: OleVariant): _Recordset;
version of the Execute method?
From the doc:
RecordsAffected indicates the number
of records, if the command operates on
data, that are affected by the command
after execution.
So that should give you what you need.
Disclaimer: I cannot test this against SQL Server (don't have it).