Need a SQL Script to check the object level page and row compression on each table for the database.
I tried using the ms_foreachdb script to loop each table to check the page compression, which is not working.
You need to combine msforeachdb with msforeachtable.
This is a very raw sample to get you started:
exec sp_MSforeachdb N'
if db_id(''¤'') > 4 -- Skip system dbs
BEGIN
use [¤]
exec sp_msforeachtable ''
print ''''¤ ?''''
declare #tb sysname = PARSENAME(''''?'''',1)
exec Sp_estimate_data_Compression_Savings #object_name = #tb, #schema_name = ''''dbo'''', #index_id = null, #partition_number = null, #data_compression=row
'',#replacechar = N''?''
END
', #replacechar = N'¤'
The biggest pain with these two procs is that amount of quotes one have to make. I think you might be better off with two nested cursors looping stuff from databases etc
EDIT: Improved script a bit. But like i said, you better be off creating a cursor script yourself
Related
My desired end result is to simply be able to SELECT from a Stored Procedure. I've searched the Internet and unfortunately the Internet said this can't be done and that you first need to create a Temp Table to store the data. My problem is that you must first define the columns in the Temp Table before Executing the STORED Procedure. This is just time consuming. I simply want to take the data from the stored procedure and just stick it into a Temp Table.
What is the FASTEST route to achieve this from a coding perspective? To put it simply it's time consuming to first have to lookup the returned fields from a Stored Procedure and then write them all out.
Is there some sort of tool that can just build the CREATE Table Statement based on the Stored Procedure? See screenshot for clarification.
Most of the Stored Procedures I'm dealing with have 50+ fields. I don't look forward to defining each of these fields manually.
Here is good SO Post that got me this far but not what I was hoping. This still takes too much time. What are experienced SQL Server guys doing? I've only just recently made the jump from Oracle to SQL Server and I see that Temp Tables are a big deal in SQL Server from what I can tell.
You have several options to ease your task. However, these won't be fully automatic. Be aware that these won't work if there's dynamic sql in the procedure's code. You might be able to format the result from the functions to increase the automation allowing you to copy and paste easily.
SELECT * FROM sys.dm_exec_describe_first_result_set_for_object(OBJECT_ID('report.MyStoredProcedureWithAnyColumns'), 0) ;
SELECT * FROM sys.dm_exec_describe_first_result_set(N'EXEC report.MyStoredProcedureWithAnyColumns', null, 0) ;
EXEC sp_describe_first_result_set #tsql = N'EXEC report.MyStoredProcedureWithAnyColumns';
GO
If you don't mind ##temp table and some dynamic SQL
NOTE: As Luis Cazares correctly pointed out... the ##temp runs the risk of collision due to concurrency concerns
Example
Declare #SQL varchar(max) = 'Exec [dbo].[prc-App-Lottery-Search] ''8117'''
Declare #temp varchar(500) = '##myTempTable'
Set #SQL = '
If Object_ID(''tempdb..'+#temp+''') Is Not NULL Drop Table '+#temp+';
Create Table '+#temp+' ('+stuff((Select concat(',',quotename(Name),' ',system_type_name)
From sys.dm_exec_describe_first_result_set(#SQL,null,null ) A
Order By column_ordinal
For XML Path ('')),1,1,'') +')
Insert '+#temp+' '+#SQL+'
'
Exec(#SQL)
Select * from ##myTempTable
I'm trying to make a simple select query on several databases on the same server, the server has 104 databases where 52 have the same schema so that is 2 different schemas (this are being generated by equipment on production floor, is a mistake that I have to handle until the equipment vendor figures how to create a single database for each scheme).
So I have a query like
select *
from TB_AOIResult
where serialnumber = 'snx'
At first I use this, it retrieves the data, but also a lot of errors as several databases on the server do not have that table .
[exec sp_MSforeachdb 'use ?;SELECT * FROM \[TB_AOIResult\] where barcode ="102564AG1710200018476"'
go]1
then
so far the only way that I have found is to declare all the 52 databases (at this moment, the machines will generate a new db per week) in this statement, that is severally impractical
declare #sql1 as VARCHAR(4000)
SET #sql1 ='IF ''?''IN(''KY_Result_201715'',''KY_Result_201714'',''KY_Result_201713'',''KY_Result_201712''[enter image description here][2])
EXECUTE(''USE [?]
SELECT * FROM [TB_AOIResult] where barcode ="102564AG1710200018476" '')'
EXEC sp_MSforeachdb #command1 = #sql1
Could someone help me and explain me if there is any other way to do this query in all the databases with the same schema and avoid to run in the ones that does not have the table, without write the name of each database?
Thanks
Finally I solve it as follows:
exec sp_MSforeachdb #command1 = 'USE ?;
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N''TB_AOIResult'') BEGIN SELECT * FROM TB_AOIResult where barcode like ''102564AG171020001847%'' END'
go
I would like to know how I can switch from one database to another within the same script. I have a script that reads the header information from a SQL Server .BAK file and loads the information into a test database. Once the information is in the temp table (Test database) I run the following script to get the database name.
This part works fine.
INSERT INTO #HeaderInfo EXEC('RESTORE HEADERONLY
FROM DISK = N''I:\TEST\database.bak''
WITH NOUNLOAD')
DECLARE #databasename varchar(128);
SET #databasename = (SELECT DatabaseName FROM #HeaderInfo);
The problem is when I try to run the following script nothing happens. The new database is never selected and the script is still on the test database.
EXEC ('USE '+ #databasename)
The goal is switch to the new database (USE NewDatabase) so that the other part of my script (DBCC CHECKDB) can run. This script checks the integrity of the database and saves the results to a temp table.
What am I doing wrong?
You can't expect a use statement to work in this fashion using dynamic SQL. Dynamic SQL is run in its own context, so as soon as it has executed, you're back to your original context. This means that you'd have to include your SQL statements in the same dynamic SQL execution, such as:
declare #db sysname = 'tempdb';
exec ('use ' + #db + '; dbcc checkdb;')
You can alternatively use fully qualified names for your DB objects and specify the database name in your dbcc command, even with a variable, as in:
declare #db sysname = 'tempdb';
dbcc checkdb (#db);
You can't do this because Exec scope is limited to dynamic query. When exec ends context is returned to original state. But context changes in Exec itself. So you should do your thing in one big dynamic statement like:
DECLARE #str NVARCHAR(MAX)
SET #str = 'select * from table1
USE DatabaseName
select * from table2'
EXEC (#str)
I have three websites which uses an abstract database structure with tables like: Items, Places, Categories, etc... and stored procedures like GetItemsByCategory, GetRelatedItems, etc... Actually im using exactly the same database structure for these 3 different websites.
From a code perspective im using the same code for all websites (except the HTML which is specific foreach one), and all the common code is in few projects used by all websites, so everytime that i detect a bug (which is in all websites) i just fix it on one place (the common part used by all) and automatically all websites get the fix.
Actually im using Asp.net MVC3 and Sql server.
Everytime i want to extend some funcionality, and i need a new table, stored procedure or something related with database, i have to do the modification in each database.
Do you know any approach that i could use to be able to have the same flexibility and do database modifications only one time for all websites?
Do you think I'm using a good approach or i should use something different in your opinion?
If the databases are on a single server, you could generate the script for the procedure from Management Studio, and make sure to use the option to "check for object existence" (Tools > Options > SQL Server Object Explorer > Scripting). This will yield something like this (most importantly it produces your stored procedure code as something you can execute using dynamic SQL):
USE DBName;
GO
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
IF NOT EXISTS (...)
BEGIN
EXEC dbo.sp_executesql #statement = N'CREATE PROCEDURE dbo.whatever ...
'
END
GO
Now that you have this script, you can modify it to work across multiple databases - you just need to swipe the #statement = portion and re-use it. First you need to stuff the databases where you want this to work into a #table variable (or you can put this in a permanent table, if you want). Then you can build a command to execute in each database, e.g.
DECLARE #dbs TABLE (name SYSNAME);
INSERT #dbs(name) SELECT N'db1';
INSERT #dbs(name) SELECT N'db2';
INSERT #dbs(name) SELECT N'db3';
-- now here is where we re-use the create / alter procedure command from above:
DECLARE #statement NVARCHAR(MAX) = N'CREATE PROCEDURE dbo.whatever ...
';
-- now let's build some dynamic SQL and run it!
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + '
EXEC ' + QUOTENAME(name) + '.dbo.sp_executesql N''' + #statement + ''';'
FROM #dbs;
EXEC sys.sp_executesql #sql;
Alternatively, you could create a custom version of my sp_msforeachdb or sp_ineachdb replacements:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server
I used to use a tool called SQLFarms Combine for this, but the tool doesn't seem to exist anymore, or perhaps it has been swallowed up / re-branded by another company. Red Gate has since produced SQL Multi Script that has similar functionality.
If you added a column to all your tables called websiteId you could just have one database. Store the unique websiteId in each site's web.config and just pass it with each request for data. Obviously each site's data is stored with their websiteId so data can be queried per website.
It means a bit of refactoring in your db and any calls to your your db, but once done, you only have one database to maintain.
Of course this is assuming your databases are on the same server...
I am writing a stored procedure for SQL Server 2008 in which I need to extract information from a set of tables. I do not know ahead of time the structure of those tables. There is another table in the same database that tells me the names and types of the fields in this table.
I am doing this:
declare #sql nvarchar(max)
set #sql = 'select ... into #new_temporary_table ...'
exec sp_executesql #sql
Then I iterate doing:
set #sql = 'insert into #another_temporary_table ... select ... from #new_temporary_table'
exec sp_executesql #sql
After that I drop the temporary table. This happens in a loop, so the table with be created, populated and dropped many times, each time with different columns.
This fails with the error:
Invalid object name: #new_temporary_table.
After some googling I have found that:
The table #new_temporary_table is being created in the scope of the call to exec sp_executesql which is different from the one of my stored proc. This is the reason the next exec sp_executesql cannot find the table. This post explains it:
http://social.msdn.microsoft.com/forums/en-US/transactsql/thread/1dd6a408-4ac5-4193-9284-4fee8880d18a
I could use global temporary tables, which are prepended with ##. I can't do this because multiple stored procs could run at the same time and they would be affecting each other's state
In this article it says that if I find myself in this situation I should change the structure of the database. This is not an option for me:
http://www.sommarskog.se/dynamic_sql.html
One workaround I have found was combining all the select into #new_temporary_table.. and all the insert into ... scripts into one gigantic statement. This works fine but it has some downsides.
If I do print #sql to troubleshoot, the text gets truncated, for example.
Do I have any other option? All ideas are welcome.
You could use global temp tables, but use a context id (such as newid()) as part of the global temp table name.
declare #sql varchar(2000)
declare #contextid varchar(50) = convert(varchar(20), convert(bigint, substring(convert(binary(16), newid()), 1, 4)))
set #sql = 'select getdate() as stuff into ##new_temporary_table_' + #contextid
exec (#sql)
I think it's best to use one single script.
You can change how many characters will print in Tools > Options > Query Results > SQL Server > Results to Text - change "Maximum number of characters..." from 256 to the max (8192).
If it's bigger than 8192, then yes, printing is difficult. But you could try a different option in this case. Instead of PRINT #sql; instead use the following (with Results to Grid):
SELECT sql FROM (SELECT #sql) AS x(sql) FOR XML PATH;
Now you can click on the result, and it opens in a new query window. Well, it's an XML file window, and you can't execute it or see color-coding, and you have to ignore that it changes e.g. > to > to make it valid as XML data, but from here it's easy to eyeball if you're just trying to eyeball it. You can copy and paste it to a real query editor window and do a search and replace for the entitized characters if you like. FWIW I asked for them to make such XML windows real query windows, but this was denied:
http://connect.microsoft.com/SQLServer/feedback/details/425990/ssms-allow-same-semantics-for-xml-docs-as-query-windows
#temp tables (not global) are available in the scope they were created and below.
So you could do something like...
while (your_condition = 1) begin
set #sql = 'select ... into #temp1 ...from blah
exec sp_do_the_inserts'
exec(#sql)
end
The sp_do_the_inserts might look like...
select * into #temp2 from #temp1
....your special logic here....
This assumes you create sp_do_the_inserts beforehand, of course.
Don't know if that serves your need.