UNION with a stored procedure - sql

This is what I am trying to do
EXEC sp1 1
SELECT * FROM x
UNION
if(#num <= 1)
EXEC sp1(2)
else
null //want to return null to stop
I could do this is with a programming language but I don't have an idea what is that I am doing wrong with programming in SQL?

This, honestly, makes no sense, and I still suggest that you use an inline Table-value function here, instead of a procedure, but you can do something like this using OPENROWSET to return the dataset from a stored procedure within a SELECT statement. It can't be parametrised though (not in the traditional sense), and if you don't understand this, don't use it.
This is pseudo SQL as well as there's a lack of enough information to provide a complete solution, such as the columns needed in the SELECTs, but it might get you there if you can comprehend it:
EXEC dbo.sp1 1;
SELECT {Columns}
FROM dbo.x
UNION ALL
SELECT {Same Columns again} --This dataset's definition must be IDENTICAL to the above against your table dbo.x
FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=Yes;Database={YourDatabase}','EXEC dbo.sp1(2);') ORS; --Assumes you are using Windows Authentication
WHERE #Num <= 1
UNION ALL
SELECT NULL,NULL,NULL{,NULL...} --Until you have you enough NULL columns
Note that in this example I am using the deprecated SQLNCLI connection manager. You should really be using MSOLEDBSQL, however, the only instance I currently have access to with a trusted connection is a 2012 instance which doesn't have that driver installed; so I didn't want to post code that I hadn't minimally tested.

You can achieve this in SQL this way:
SELECT * into #temp FROM x
if(#num <= 1)
begin
insert into #temp
EXEC sp1(2)
select * from #temp
end
else
begin
select null
end
First you create a temp table and insert x table records into it and after that you check your condition and then insert records from procedure and then select * from #temp and other case it will return null.

Related

Select all record from all the tables, every derived table must have its own alias

I'm working on a e-learning project in which there is a table named chapter in which there is a column named question_table this is table in which the specific chapter's questions are added.
Now the problem is I want to display all the question from all the chapter for this I used following sql query
SELECT * FROM (SELECT `question_table` FROM `chapter`)
but it doesn't work and gives the error:
"Every derived table must have its own alias".
Note: I want to do it using SQL not PHP.
Firstly, I think you would be better redesigning your database. Multiple tables of the same structure holding the same data are generally not a good idea.
However what you require is possible using a MySQL procedure to build up some dynamic SQL and then execute it, returning the resulting data.
A procedure as follows could be used to do this:-
DROP PROCEDURE IF EXISTS dynamic;
delimiter //
CREATE PROCEDURE dynamic()
BEGIN
DECLARE question_table_value VARCHAR(25);
DECLARE b INT DEFAULT 0;
DECLARE c TEXT DEFAULT '';
DECLARE cur1 CURSOR FOR SELECT `question_table` FROM `chapter`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1;
OPEN cur1;
SET b = 0;
WHILE b = 0 DO
FETCH cur1 INTO question_table_value;
IF b = 0 THEN
IF c = '' THEN
SET c = CONCAT('SELECT * FROM `',question_table_value, '`');
ELSE
SET c = CONCAT(c, ' UNION SELECT * FROM `',question_table_value, '`');
END IF;
END IF;
END WHILE;
CLOSE cur1;
SET #stmt1 := c;
PREPARE stmt FROM #stmt1;
EXECUTE stmt;
END
This is creating a procedure called dynamic. This takes no parameters. It sets up a cursor to read the question_table column values from the chapter table. It looks around the results from that, building up a string which contains the SQL, which is a SELECT from each table with the results UNIONed together. This is then PREPAREd and executed. The procedure will return the result set from the SQL executed by default.
You can call this to return the results using:-
CALL dynamic()
Down side is that this isn't going to give nice results if there are no rows to return and they are not that easy to maintain or debug with the normal tools developers have. Added to which very few people have any real stored procedure skills to maintain it in future.
In MySQL you must give every subquery ("derived table") an alias:
SELECT * FROM (SELECT question_table FROM chapter) t --notice the alias "t"
The derived table here is the result of the (SELECT ...). You need to give it an alias, like so:
SELECT * FROM (SELECT question_table FROM chapter) X;
Edit, re dynamic tables
If you know all the tables in advance, you can union them, i.e.:
SELECT * FROM
(
SELECT Col1, Col2, ...
FROM Chapter1
UNION
SELECT Col1, Col2, ...
FROM Chapter2
UNION
...
) X;
SqlFiddle here
To do this solution generically, you'll need to use dynamic sql to achieve your goal.
In general however, this is indicative of a smell in your table design - your chapter data should really be in one table, and e.g. classified by the chapter id.
If you do need to shard data for scale or performance reasons, the typical mechanism for doing this is to span multiple databases, not tables in the same database. MySql can handle large numbers of rows per table, and performance won't be an issue if the table is indexed appropriately.

SELECT against stored procedure SQL Server

SELECT Val from storedp_Value within the query editor of SQL Server Management Studio, is this possible?
UPDATE
I tried to create a temp table but it didn't seem to work hence why I asked here.
CREATE TABLE #Result
(
batchno_seq_no int
)
INSERT #Result EXEC storedp_UPDATEBATCH
SELECT * from #Result
DROP TABLE #Result
RETURN
Stored Procedure UpdateBatch
delete from batchno_seq;
insert into batchno_seq default values;
select #batchno_seq= batchno_seq_no from batchno_seq
RETURN #batchno_seq
What am I doing wrong and how do I call it from the query window?
UPDATE #2
Ok, I'd appreciate help on this one, direction or anything - this is what I'm trying to achieve.
select batchno_seq from (delete from batchno_seq;insert into batchno_seq default values;
select * from batchno_seq) BATCHNO
INTO TEMP_DW_EKSTICKER_CLASSIC
This is part of a larger select statement. Any help would be much appreciated. Essentially this SQL is broken as we've migrated for Oracle.
Well, no. To select from a stored procedure you can do the following:
declare #t table (
-- columns that are returned here
);
insert into #t(<column list here>)
exec('storedp_Value');
If you are using the results from a stored procedure in this way and you wrote the stored procedure, seriously consider changing the code to be a view or user defined function. In many cases, you can replace such code with a simpler, better suited construct.
This is not possible in sql server, you can insert the results into a temp table and then further query that
CREATE TABLE #temp ( /* columns */ )
INSERT INTO #temp ( /* columns */ )
EXEC sp_MyStoredProc
SELECT * FROM #temp
WHERE 1=1
DROP TABLE #temp
Or you can use OPENQUERY but this requires setting up a linked server, the SQL is
SELECT * FROM (ThisServer, 'Database.Schema.ProcedureName <params>')
The best article (in my opinion) about all possible methods for sharing data between stored procedures in SQL Server you can find here: http://www.sommarskog.se/share_data.html
Try this
Change 'Return'
delete from batchno_seq;
insert into batchno_seq default values;
select #batchno_seq= batchno_seq_no from batchno_seq
RETURN #batchno_seq
to 'Select'
delete from batchno_seq;
insert into batchno_seq default values;
select #batchno_seq= batchno_seq_no from batchno_seq
SELECT #batchno_seq
My approach
select * into new_table from (select t1.col1,t1.col2,..
from table1 t1
union
select t2.cola,t2.colb,..
from table2 t2) as union_table
I MUST be missing something.
Since your stored procedure does not return a result set, and instead returns an integer, using the RETURN functionality of stored procs, you simply CANNOT INSERT into ANY table (since there isn't any result set coming back, at all).
BUT, you can (assuming that this is done iteratively, and not over a set) simply store the return value into a local variable, and insert that variable's value into whatever table is necessary.
However, if you simply want to return the value in the results of a Query Window in SSMS, doing the INSERT and SELECTING is overkill.
It seems to me like THIS would suffice (in a query window):
DECLARE #RetVal INT = 0;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
EXEC #RetVal = storedp_UPDATEBATCH;
COMMIT TRANSACTION;
SELECT #RetVal;
--OR
--PRINT #RetVal;
If this is way far off base, please provide the DDL for "batchno_seq", maybe I can be of better assistance that way.
Cheers!

Efficient SQL test query or validation query that will work across all (or most) databases

Many database connection pooling libraries provide the ability to test their SQL connections for idleness. For example, the JDBC pooling library c3p0 has a property called preferredTestQuery, which gets executed on the connection at configured intervals. Similarly, Apache Commons DBCP has validationQuery.
Many example queries I've seen are for MySQL and recommend using SELECT 1; as the value for the test query. However, this query doesn't work on some databases (e.g. HSQLDB, for which SELECT 1 expects a FROM clause).
Is there a database-agnostic query that's equivalently efficient but will work for all SQL databases?
Edit:
If there's not (which seems to be the case), can somebody suggest a set of SQL queries that will work for various database providers? My intention would be to programmatically determine a statement I can use based on my database provider configuration.
After a little bit of research along with help from some of the answers here:
SELECT 1
H2
MySQL
Microsoft SQL Server (according to NimChimpsky)
PostgreSQL
SQLite
Hive
SELECT 1 FROM DUAL
Oracle
SELECT 1 FROM any_existing_table WHERE 1=0
or
SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS
or
CALL NOW()
HSQLDB (tested with version 1.8.0.10)
Note: I tried using a WHERE 1=0 clause on the second query, but it didn't work as a value for Apache Commons DBCP's validationQuery, since the query doesn't return any rows
VALUES 1 or SELECT 1 FROM SYSIBM.SYSDUMMY1
Apache Derby (via daiscog)
SELECT 1 FROM SYSIBM.SYSDUMMY1
DB2
select count(*) from systables
Informix
If your driver is JDBC 4 compliant, there is no need for a dedicated query to test connections. Instead, there is Connection.isValid to test the connection.
JDBC 4 is part of Java 6 from 2006 and you driver should support this by now!
Famous connection pools, like HikariCP, still have a config parameter for specifying a test query but strongly discourage to use it:
🔠connectionTestQuery
If your driver supports JDBC4 we strongly
recommend not setting this property. This is for "legacy" databases
that do not support the JDBC4 Connection.isValid() API. This is the
query that will be executed just before a connection is given to you
from the pool to validate that the connection to the database is still
alive. Again, try running the pool without this property, HikariCP
will log an error if your driver is not JDBC4 compliant to let you
know. Default: none
Unfortunately there is no SELECT statement that will always work regardless of database.
Most databases support:
SELECT 1
Some databases don't support this but have a table called DUAL that you can use when you don't need a table:
SELECT 1 FROM DUAL
MySQL also supports this for compatibility reasons, but not all databases do. A workaround for databases that don't support either of the above is to create a table called DUAL that contains a single row, then the above will work.
HSQLDB supports neither of the above, so you can either create the DUAL table or else use:
SELECT 1 FROM any_table_that_you_know_exists_in_your_database
The jOOQ manual's section about the DUAL table lists the following for jOOQ's select(inline(1)) query:
-- Access
SELECT 1 FROM (SELECT count(*) dual FROM MSysResources) AS dual
-- BigQuery, CockroachDB, Exasol, H2, Ignite, MariaDB, MySQL, PostgreSQL,
-- Redshift, Snowflake, SQLite, SQL Server, Sybase ASE, Vertica
SELECT 1
-- MemSQL, Oracle
SELECT 1 FROM DUAL
-- CUBRID
SELECT 1 FROM db_root
-- Db2
SELECT 1 FROM SYSIBM.DUAL
-- Derby
SELECT 1 FROM SYSIBM.SYSDUMMY1
-- Firebird
SELECT 1 FROM RDB$DATABASE
-- HANA, Sybase SQL Anywhere
SELECT 1 FROM SYS.DUMMY
-- HSQLDB
SELECT 1 FROM (VALUES(1)) AS dual(dual)
-- Informix
SELECT 1 FROM (SELECT 1 AS dual FROM systables WHERE (tabid = 1)) AS dual
-- Ingres, Teradata
SELECT 1 FROM (SELECT 1 AS "dual") AS "dual"
I use this one:
select max(table_catalog) as x from information_schema.tables
to check connection and ability to run queries (with 1 row as result) for postgreSQL, MySQL and MSSQL.
I use
Select COUNT(*) As X From INFORMATION_SCHEMA.SYSTEM_USERS Where 1=0
for hsqldb 1.8.0
For tests using select count(*), it should be more efficient to use select count(1) because * can cause it to read all the column data.
select 1 would work in sql server, not sure about the others.
Use standard ansi sql to create a table and then query from that table.
Assuming the OP wants a Java answer:
As of JDBC3 / Java 6 there's the isValid() method which should be used rather than inventing one's own method.
The implementer of the driver is required to execute some sort of query against the database when this method id called. You - as a mere JDBC user - do not have to know or understand what this query is. All you have to do is to trust that the creator of the JDBC driver has done his/her work properly.
How about
SELECT user()
I use this before.MySQL, H2 is OK, I don't know others.
Just found out the hard way that it is
SELECT 1 FROM DUAL
for MaxDB as well.
For Oracle the high performing query will be
select 'X' from <your_small_table> where <primay_key_coulmn> = <some_value>
This is from a performance perspective.
I use this for Firebird
select 1 from RDB$RELATION_FIELDS rows 1
For MSSQL.
This helped me determine if linked servers were alive. Using an Open Query connection and a TRY CATCH to put the results of the error to something useful.
IF OBJECT_ID('TEMPDB..#TEST_CONNECTION') IS NOT NULL DROP TABLE #TEST_CONNECTION
IF OBJECT_ID('TEMPDB..#RESULTSERROR') IS NOT NULL DROP TABLE #RESULTSERROR
IF OBJECT_ID('TEMPDB..#RESULTSGOOD') IS NOT NULL DROP TABLE #RESULTSGOOD
DECLARE #LINKEDSERVER AS VARCHAR(25) SET #LINKEDSERVER = 'SERVER NAME GOES HERE'
DECLARE #SQL AS VARCHAR(MAX)
DECLARE #OPENQUERY AS VARCHAR(MAX)
--IF OBJECT_ID ('dbo.usp_GetErrorInfo', 'P' ) IS NOT NULL DROP PROCEDURE usp_GetErrorInfo;
--GO
---- Create procedure to retrieve error information.
--CREATE PROCEDURE dbo.usp_GetErrorInfo
--AS
--SELECT
-- ERROR_NUMBER() AS ErrorNumber
-- ,ERROR_SEVERITY() AS ErrorSeverity
-- ,ERROR_STATE() AS ErrorState
-- ,ERROR_PROCEDURE() AS ErrorProcedure
-- ,ERROR_LINE() AS ErrorLine
-- ,ERROR_MESSAGE() AS Message;
--GO
BEGIN TRY
SET #SQL='
SELECT 1
'''
--SELECT #SQL
SET #OPENQUERY = 'SELECT * INTO ##TEST_CONNECTION FROM OPENQUERY(['+ #LINKEDSERVER +'],''' + #SQL + ')'
--SELECT #OPENQUERY
EXEC(#OPENQUERY)
SELECT * INTO #TEST_CONNECTION FROM ##TEST_CONNECTION
DROP TABLE ##TEST_CONNECTION
--SELECT * FROM #TEST_CONNECTION
END TRY
BEGIN CATCH
-- Execute error retrieval routine.
IF OBJECT_ID('dbo.usp_GetErrorInfo') IS NOT NULL -- IT WILL ALWAYS HAVE SOMTHING...
BEGIN
CREATE TABLE #RESULTSERROR (
[ErrorNumber] INT
,[ErrorSeverity] INT
,[ErrorState] INT
,[ErrorProcedure] INT
,[ErrorLine] INT
,[Message] NVARCHAR(MAX)
)
INSERT INTO #RESULTSERROR
EXECUTE dbo.usp_GetErrorInfo
END
END CATCH
BEGIN
IF (Select ERRORNUMBER FROM #RESULTSERROR WHERE ERRORNUMBER = '1038') IS NOT NULL --'1038' FOR ME SHOWED A CONNECTION ATLEAST.
SELECT
'0' AS [ErrorNumber]
,'0'AS [ErrorSeverity]
,'0'AS [ErrorState]
,'0'AS [ErrorProcedure]
,'0'AS [ErrorLine]
, CONCAT('CONNECTION IS UP ON ', #LINKEDSERVER) AS [Message]
ELSE
SELECT * FROM #RESULTSERROR
END
learn.microsoft.com

Querying results of a stored procedure

I have a stored procedure that returns a large number of results, and would like a better way to debug/ parse the results than copy/pasting into excel or whatever - is there a way to pass the results of the procedure into a query? e.g., if the procedure call was something like:
exec database..proc 'arg1','arg2','arg3'
my thought was to do something like:
select distinct column1 from
(exec database..proc 'arg1','arg2','arg3')
which clearly did not work, or I wouldn't be here. If it matters, this is for a sybase database.
Thanks!
The code below works in MS SQL 2005. I don't have a Sybase installation right now to test it on that. If it works in Sybase you could use a temporary table (or permanent table) outside of your stored procedure so that you don't have alter the code that you're trying to test (not a very good testing procedure generally.)
CREATE TABLE dbo.Test_Proc_Results_To_Table
(
my_id INT NOT NULL,
my_string VARCHAR(20) NOT NULL
)
GO
CREATE PROCEDURE dbo.Test_Proc_Results_To_Table_Proc
AS
BEGIN
SELECT
1 AS my_id,
'one' AS my_string
END
GO
INSERT INTO dbo.Test_Proc_Results_To_Table (my_id, my_string)
EXEC dbo.Test_Proc_Results_To_Table_Proc
GO
SELECT * FROM dbo.Test_Proc_Results_To_Table
GO
In SQL Anywhere 10 and 11(didn't see whether it's ASA or ASE you're asking about):
SELECT DISTINCT Column1
FROM procName(parameter1, parameter2, parameter3);
I don't have ASE, and I'm not sure if this works on earlier ASA versions.
you could create a temporary table (#temp) in the sp and populate the result set in there. You can later select from the same temp table from the same session. (Or use a global temp table in sybase with ##temp syntax)
This is because what you want to do (select * from exec sp) is not directly possible in sybase
Is it possible to rewrite the stored proc as a function that returns a table? On SQL Server this is certainly possible. Then you can do...
select
<any columns you like>
from
dbo.myFunc( 'foo', 'bar', 1 )
where
<whatever clauses you like>
order by
<same>
I'm not familiar with Sybase, but in MySQL you could use the IN parameter to write one SQL query for all this. Ex:
select distinct column1 from table where column1 in (your_first_query_with_all_the_arguments)
I don't have Sybase installed right now, so some minor syntactic aspect may be wrong here - I can't check, but I used it extensively in the past: select * into #temp from proc_name('arg1','arg2','arg3') should create the local temp table for you automatically with the right columns. Within the same transaction or begin/end block you can access #temp by select * from #temp.
The only real way around this problem is to create a table in your database to store temporary values.
Lets say the stored procedures selects Column1, Column2 & Column3.
Have a table (tempTable) with Column1, Column2, Column3 and set your stored procedure to the following:
CREATE PROCEDURE database..proc
AS
BEGIN
DELETE FROM tempTable
INSERT INTO tempTable (Column1, Column2, Column3)
SELECT Column1, Column2, Column3
FROM Table1
END
then for your sql code to select the values have:
exec database..proc
SELECT Column1, Column2, Column3
FROM tempTable
I hope this helps, I've come across similar problems before and this was the best I could work out.

Handling the data in an IN clause, with SQL parameters?

We all know that prepared statements are one of the best way of fending of SQL injection attacks. What is the best way of creating a prepared statement with an "IN" clause. Is there an easy way to do this with an unspecified number of values? Take the following query for example.
SELECT ID,Column1,Column2 FROM MyTable WHERE ID IN (1,2,3)
Currently I'm using a loop over my possible values to build up a string such as.
SELECT ID,Column1,Column2 FROM MyTable WHERE ID IN (#IDVAL_1,#IDVAL_2,#IDVAL_3)
Is it possible to use just pass an array as the value of the query paramter and use a query as follows?
SELECT ID,Column1,Column2 FROM MyTable WHERE ID IN (#IDArray)
In case it's important I'm working with SQL Server 2000, in VB.Net
Here you go - first create the following function...
Create Function [dbo].[SeparateValues]
(
#data VARCHAR(MAX),
#delimiter VARCHAR(10)
)
RETURNS #tbldata TABLE(col VARCHAR(10))
As
Begin
DECLARE #pos INT
DECLARE #prevpos INT
SET #pos = 1
SET #prevpos = 0
WHILE #pos > 0
BEGIN
SET #pos = CHARINDEX(#delimiter, #data, #prevpos+1)
if #pos > 0
INSERT INTO #tbldata(col) VALUES(LTRIM(RTRIM(SUBSTRING(#data, #prevpos+1, #pos-#prevpos-1))))
else
INSERT INTO #tbldata(col) VALUES(LTRIM(RTRIM(SUBSTRING(#data, #prevpos+1, len(#data)-#prevpos))))
SET #prevpos = #pos
End
RETURN
END
then use the following...
Declare #CommaSeparated varchar(50)
Set #CommaSeparated = '112,112,122'
SELECT ID,Column1,Column2 FROM MyTable WHERE ID IN (select col FROM [SeparateValues](#CommaSeparated, ','))
I think sql server 2008 will allow table functions.
UPDATE
You'll squeeze some extra performance using the following syntax...
SELECT ID,Column1,Column2 FROM MyTable
Cross Apply [SeparateValues](#CommaSeparated, ',') s
Where MyTable.id = s.col
Because the previous syntax causes SQL Server to run an extra "Sort" command using the "IN" clause. Plus - in my opinion it looks nicer :D!
If you would like to pass an array, you will need a function in sql that can turn that array into a sub-select.
These functions are very common, and most home grown systems take advantage of them.
Most commercial, or rather professional ORM's do ins by doing a bunch of variables, so if you have that working, I think that is the standard method.
You could create a temporary table TempTable with a single column VALUE and insert all IDs. Then you could do it with a subselect:
SELECT ID,Column1,Column2 FROM MyTable WHERE ID IN (SELECT VALUE FROM TempTable)
Go with the solution posted by digiguru. It's a great reusable solution and we use the same technique as well. New team members love it, as it saves time and keeps our stored procedures consistent. The solution also works well with SQL Reports, as the parameters passed to stored procedures to create the recordsets pass in varchar(8000). You just hook it up and go.
In SQL Server 2008, they finally got around to addressing this classic problem by adding a new "table" datatype. Apparently, that lets you pass in an array of values, which can be used in a sub-select to accomplish the same as an IN statement.
If you're using SQL Server 2008, then you might look into that.
Here's one technique I use
ALTER Procedure GetProductsBySearchString
#SearchString varchar(1000),
as
set nocount on
declare #sqlstring varchar(6000)
select #sqlstring = 'set nocount on
select a.productid, count(a.productid) as SumOf, sum(a.relevence) as CountOf
from productkeywords a
where rtrim(ltrim(a.term)) in (''' + Replace(#SearchString,' ', ''',''') + ''')
group by a.productid order by SumOf desc, CountOf desc'
exec(#sqlstring)