I am having a problem with using the sp_msforeachtable SQL Server stored procedure.
I want to have output of EXECUTE statement in one table using following SQL query:
TRUNCATE TABLE DaneOSpuscie --clear the table
EXEC sp_msforeachtable
'INSERT INTO [DaneOSpuscie] ([Database name], [Table name])
SELECT ''?'', name
FROM [sys].[columns]
WHERE [sys].[columns].[name] LIKE ''%[Tt][aA][pP12]%'' '
This throws an error
String or binary data would be truncated
for each table.
Additional info: Table "DaneOSpuscie" has only two columns: Database name, Table name of type varchar.
Check your table structure.
Check your max length of your objects.
Compare it. Set your columns in your table to real max length or use maximal system object length - sysname = nvarchar(128).
Related
I want to create a view as follows:
CREATE view [dbo].[test] as
SELECT TEST.*
FROM OPENQUERY([MyServerLink],'LONGSELECTQUERY') as TEST;
The SELECT statement exceeds 8000 characters long which gives me that error:
SQL Error [103] [S0001]: The character string that starts with 'SELECT ...' is too long. Maximum length is 8000.
How can I overcome that and create my view?
You can create a temporary table on the linked server and then use the temporary table
Example (MySQL)
EXEC('DROP TEMPORARY TABLE IF EXISTS TEMP_LONGSELECTQUERY') AT MyServerLink
EXEC('
CREATE TEMPORARY TABLE TEMP_LONGSELECTQUERY AS
LONGSELECTQUERY
') AT MyServerLink
SELECT * FROM OPENQUERY(MyServerLink, 'SELECT * FROM TEMP_LONGSELECTQUERY')
But, if the linked server is also a MSSQL Server you can make a select refering to the Server.Database.Schema.Table:
SELECT
/******/
FROM MyServerLink.master.dbo.xxx x
join MyServerLink.master.dbo.yyy y /*****/
I'm able to query all the databases via How to run the same query on all the databases on an instance? but not sure how to run the query by matching a specific column and value.
example, assume column name is code. query + code='THBN'
Thanks.
EXECUTE sp_MSForEachDB
'USE ?;
SELECT * from table where code=''tbhn''';
you could also ignore system databases like below
EXECUTE sp_MSForEachDB
'if db_id()>4
begin
SELECT * from table where code=''tbhn''
end';
For my SQL Server unit tests, I am trying to suppress returning all rows when counting results of a "get all rows" stored procedures. To this end I am trying to write the results to a temp table.
I begin with a linked server entry pointing to the current server and database.
IF NOT EXISTS (SELECT * FROM sys.servers WHERE NAME = 'DB_LOCAL')
BEGIN
DECLARE #DBNAME VARCHAR(MAX) = (SELECT DB_NAME())
EXEC sp_addlinkedserver #server = 'DB_LOCAL', #srvproduct = '', #provider = 'SQLOLEDB', #datasrc = ##servername, #catalog=#DBNAME
END
This is so that I can use OPENQUERY to create a temp table with the output structure of the stored procedure on the fly without having to hard code the columns.
IF OBJECT_ID('tempdb..#ut_results') IS NOT NULL
DROP TABLE #ut_results
SELECT *
INTO #ut_results
FROM OPENQUERY (DB_LOCAL, 'EXEC p_AnzLookup_get #id = 0')
I want to use this temp table to exec my stored procs into, I can't use OPENQUERY again because I need to specify variables as part of the test. Also, the unit test is in a transaction and doing so creates locking issues. Once I have the structure I do this. I can't specify the column names without the timestamp column which I appreciate would work as they could be changed by 3rd parties.
TRUNCATE TABLE #ut_results
INSERT INTO #ut_results
EXEC p_AnzLookup_get #id = #record_id
This insert into is failing because I have a timestamp column returned by the stored procedure.
Msg 273, Level 16, State 1, Line 2
Cannot insert an explicit value into a timestamp column. Use INSERT with a column list to exclude the timestamp column, or insert a DEFAULT into the timestamp column.
I can't change the timestamp column in the temp table due to this error.
ALTER TABLE #ut_results
ALTER COLUMN TStamp BINARY(8)
Msg 4928, Level 16, State 1, Line 5
Cannot alter column 'TStamp' because it is 'timestamp'.
I can't drop and recreate the timestamp column in the temp table because it changes the column order.
ALTER TABLE #ut_results DROP COLUMN TStamp
ALTER TABLE #ut_results ADD TStamp BINARY(8)
Which leads to different errors when data inserts into the wrong columns:
Msg 257, Level 16, State 3, Procedure p_AnzLookup_get, Line 20
Implicit conversion from data type datetime to int is not allowed. Use the CONVERT function to run this query.
I can't make changes to stored procs these unit tests are for and I can't hard code the column names. I need to write this in a way that is both resilient an reactive to changes outside of my control.
This is just a small subset of one of the unit tests that I have extracted to demonstrate this problem. Any thoughts as to how I get round this sticky bit?
I think I have something that will work. Create temp table using OPENQUERY then mash that through XML to get a csv list of column names into a varchar. Then do a replace on this to cast the timestamp column as varbinary(8).
Using the column list from the above I can select the structure of my first temp table into the second with the fields in the correct order and the timestamp column defined as varbinary(8) that can accept timestamp data.
It isnt pretty tho:
IF OBJECT_ID('tempdb..#ut_results') IS NOT NULL DROP TABLE #ut_results
IF OBJECT_ID('tempdb..#ut_results2') IS NOT NULL DROP TABLE #ut_results2
SELECT * INTO #ut_results2
FROM OPENQUERY ( DB_LOCAL ,'EXEC p_AnzLookup_get #id=-1' )
DECLARE #cols VARCHAR(MAX) = ''
SELECT #cols = Stuff(( select ',' + name from tempdb.sys.columns where object_id =
object_id('tempdb..#ut_results2')
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
SET #cols = REPLACE(#cols,'TStamp','CAST(TStamp AS VARBINARY(8)) AS TStamp')
DECLARE #SQl VARCHAR(MAX) = 'SELECT ' + #cols + ' INTO #ut_results FROM #ut_results2 WHERE 1=2'
EXEC (#SQl)
IF OBJECT_ID('tempdb..#ut_results2') IS NOT NULL DROP TABLE #ut_results2
EDIT:
One slight change to this. In the above I create the final temp table in dynamic SQL but it was not accessible afterwards. I have changed this to a regular table.
DECLARE #SQl VARCHAR(MAX) = 'SELECT ' + #cols + ' INTO ut_results FROM #ut_results2'
EXECUTE (#SQl)
Note: the highest linked question does not solve the problem for system stored procedures, but it's close. With help of the commenters, I came to a working answer.
Trying to use statements such as the following for sp_spaceused, throws an error
SELECT * INTO #tblOutput exec sp_spaceused 'Account'
SELECT * FROM #tblOutput
The errors:
Must specify table to select from.
and:
An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements, look for empty alias names. Aliases defined as "" or [] are not allowed. Change the alias to a valid name.
When I fully declare a table variable, it works as expected, so it seems to me that the stored procedure does return an actual table.
CREATE TABLE #tblOutput (
name NVARCHAR(128) NOT NULL,
rows CHAR(11) NOT NULL,
reserved VARCHAR(18) NOT NULL,
data VARCHAR(18) NOT NULL,
index_size VARCHAR(18) NOT NULL,
unused VARCHAR(18) NOT NULL)
INSERT INTO #tblOutput exec sp_spaceused 'Response'
SELECT * FROM #tblOutput
Why is it not possible to use a temp table or table variable with the result set of EXECUTE sp_xxx? Or: does a more compact expression exist than having to predefine the full table each time?
(incidentally, and off-topic, Googling for the exact term SELECT * INTO #tmp exec sp_spaceused at the time of writing, returned exactly one result)
TL;DR: use SET FMTONLY OFF with OPENQUERY, details below.
It appears that the link provided by Daniel E. is only part of the solution. For instance, if you try:
-- no need to use sp_addlinkedserver
-- must fully specify sp_, because default db is master
SELECT * FROM OPENQUERY(
[SERVERNAME\SQL2008],
'exec somedb.dbo.sp_spaceused ''Account''')
you will receive the following error:
The OLE DB provider "SQLNCLI10" for linked server "LOCALSERVER\SQL2008" supplied inconsistent metadata for a column. The name was changed at execution time.
I found the solution through this post, and then a blog-post on OPENQUERY, which in turn told me that until SQL2008, you need to use SET FMTONLY OFF. The final solution, which is essentially surprisingly simple (and easier to accomplish since there is no need to specify a loopback linked server), is this:
SELECT * FROM OPENQUERY(
[SERVERNAME\SQL2008],
'SET FMTONLY OFF
EXEC somedb.dbo.sp_spaceused ''Account''')
In addition, if you haven't set DATA-ACCESS, you may get the following error:
Server 'SERVERNAME\SQL2008' is not configured for DATA ACCESS.
This can be remedied by running the following command:
EXEC sp_serveroption 'SERVERNAME\SQL2008', 'DATA ACCESS', TRUE
We cannot SELECT from a stored procedure thats why SELECT * INTO ..Exec sp_ will not work.
To get the result set returned from a store procedure we can INSERT INTO a table.
SELECT INTO statement creates a table on fly and inserts data from the source table/View/Function. The only condition is source table should exist and you should be able to Select from it.
Sql Server doesn't allow you to use SELECT from sp_ therefore you can only use the INSERT INTO statement when executing a stored procedure this means at run time you can add the returned result set into a table and Select from that table at later stage.
INSERT INTO statement requires the destination table name, An existing table. Therefore whether you use a Temp Table, Table variable or Sql server persistent table you will need to create the table first and only they you can use the syntax
INSERT INTO #TempTable
EXECUTE sp_Proc
Using [YOUR DATABASE NAME]
CREATE TABLE [YOURTABLENAME]
(Database_Name Varchar(128),
DataBase_Size VarChar(128),
unallocated_Space Varchar(128),
reserved Varchar(128),
data Varchar(128),
index_size Varchar(128),
unused Varchar(128)
);
INSERT INTO dbo.[YOUR TABLE NAME]
(
Database_Name,
DataBase_Size,
unallocated_Space,
reserved,
data,
index_size,
unused
)
EXEC sp_spaceused #oneresultset = 1
--To get it to return it all as one data set add the nonresultset=1 at the end and viola good to go for writing to a table. :)
I'm having an issue with creating dynamic sql statement in SQL Anywhere.
CREATE PROCEDURE pplAnalysis
AS
BEGIN
DECLARE #Sql NVARCHAR(4000)
SELECT #Sql = "select * from cds.ppl"
EXECUTE(#Sql)
END
When I execute this procedure, I get an Column 'select * from cds.ppl' not found error.
Can you please tell me what am I doing wrong?
The issue had to do with syntax and the RESULT clause; after adding semicolons, RESULT clause, and used SET to initialize the Sql variable, here is what worked (tested in SQL Anywhere Network Server Version 12.0.1):
drop proc pplAnalysis;
CREATE PROCEDURE pplAnalysis()
RESULT (cnot CHAR(5), cnull CHAR(5), vnot VARCHAR(5), vnull VARCHAR(5), explanation VARCHAR(50))
BEGIN
DECLARE #Sql NVARCHAR(4000);
SET #Sql = 'select cnot, cnull, vnot, vnull, explanation from dbo.spaces';
EXECUTE ( #Sql );
END;
spaces is a table in the dbo schema and those columns are the same type specified in RESULT
Tested these two ways to execute the procedure and both returned result:
call pplAnalysis();
cnot cnull vnot vnull explanation
----- ----- ----- ----- --------------------------------------------------
Execution time: 0.027 seconds
Procedure completed
or
exec pplAnalysis;
cnot cnull vnot vnull explanation
----- ----- ----- ----- --------------------------------------------------
Execution time: 0.018 seconds
For more details:
Returning result sets from procedures
Create procedure statement
Try first saving the result of the query in a temporal table, and then do a SELECT from the temporal table:
SELECT #Sql = "select into #temp * from cds.ppl"
EXECUTE(#Sql)
SELECT * FROM #temp
Use single quotes.
SELECT #Sql = 'select * from cds.ppl'
After some research, I have edited my answer.
Regarding the EXECUTE ( string-expression ) statement, yes you have to use single quotes instead of double quotes for the string expression. This page mentions:
It lets you execute dynamically prepared statements, such as
statements that are constructed using the parameters passed in to a
procedure. Literal strings in the statement must be enclosed in single
quotes, and the statement must be on a single line.
Which will eliminate the column not found error but the procedure will return this other error:
Result set not permitted in '<batch statement>'
Same error returned when trying to execute this statement alone:
execute ('select * from sysusers')
With probable cause:
You attempted to execute a SELECT statement in a context where a
result set is not permitted.
See my most recent answer for the solution.
And regarding schemas, here's how to refer to objects:
It is always good practice to refer to database objects by a schema
name and the object name, separated by a period (.). For a complete example, to SELECT records from the Employee table in the HumanResources schema of the current database would look like:
SELECT * FROM HumanResources.Employee
To reference an object located in a remote database, the fully
qualified object name includes the server name and the database name.
For example, to SELECT records from the Employee table in the
HumanResources schema in the AdventureWorks database on MyServer would
look like:
SELECT * FROM MyServer.AdventureWorks.HumanResources.Employee
I tested that in SQL Anywhere 12 and it works the same. And even though I was not familiar with schemas, what I'm suggesting you below is actually using schemas, dbowner would be the schema name:
1) select * from dbname.dbowner.tablename
2) select * from dbowner.tablename
3) select * from dbname..tablename (assumes table exists in the dbo schema)
Bottom line.... In your select statement cds.ppl has to be a table named ppl created in the cds schema.
Or if cds is your database name and ppl your table name created in the dbo schema, you are missing a dot:
select * from cds..ppl