I'm trying to write a query that will select data from a table. due to different versions of the database, there are 2 possible structures for the source table, where the newer version has 2 more fields than the old one.
I've tried identifying the older structure and replacing the columns with NULL and also tried writing 2 separate queries with and IF statement directing to the correct one. Neither of these solutions work and in both cases it seems that the SQL engine is failing on validating these 2 columns.
Examples of my attempted solutions:
IF NOT EXISTS (SELECT *
FROM sys.objects
WHERE object_id = Object_id(N'[dbo].[Test2]')
AND type IN ( N'U' ))
BEGIN
CREATE TABLE [dbo].[test2]
(
[id] [INT] IDENTITY(1, 1) NOT NULL,
[statusid] [INT] NULL
)
END
go
DECLARE #Flag INT = 0
IF EXISTS(SELECT 1
FROM sys.columns
WHERE NAME = N'TestId'
AND object_id = Object_id(N'dbo.Test2'))
SET #Flag = 1
--Solution #1
IF #Flag = 1
SELECT id,
statusid,
testid
FROM dbo.test2
ELSE
SELECT id,
statusid
FROM dbo.test2
--Solution #2
SELECT id,
statusid,
CASE
WHEN #Flag = 1 THEN testid
ELSE NULL
END AS TestId
FROM dbo.test2
you can use Dynamic SQL and generate the query accordingly depends on value of #flag
declare #sql nvarchar(max)
select #sql = N'select id, statusid, '
+ case when #flag = 1 then 'testid' else 'NULL' end + ' as testid'
+ ' from dbo.test2'
print #sql
exec sp_executesql #sql
But it will not be that easy to code and maintain Dynamic Query if you have a complex query.
I have the following query. can someone help rapping this in a cursor.
IF OBJECT_ID('tempdb.[dbo].[#Results]') IS NOT NULL
DROP TABLE [dbo].[#Results]
GO
CREATE TABLE [dbo].[#Results] (
[DatabaseName] VARCHAR(128) NULL,
[DatabaseVersion] VARCHAR(128) NULL,
[DateChangedOn] DATETIME NULL)
EXEC sp_msForEachDb '
IF EXISTS (SELECT * FROM [?].sys.objects WHERE NAME = ''stdVersions'')
BEGIN
INSERT INTO #Results
SELECT
''?'' AS [DatabaseName],
v.[DatabaseVersion],
l.[DateChangedOn]
FROM [?].dbo.stdVersions v
CROSS JOIN [?].dbo.stdChangeLog l
END'
SELECT * FROM #Results ORDER BY DateChangedOn
the query should return list of all database names in the server and within the databases return the databaseversion column and DateChangeOn Column. All the databases in the server contain tables named stdVersions and stdChangeLog of which the stdVersion table have a single row of DatabaseVersion comes and stdChangeLog table have a single row of DateChangedOn.
Below is an example of creating a cursor on the #Results table.
DECLARE #DatabaseName sysname
, #DatabaseVersion sysname
, #DateChangedOn datetime;
DECLARE DatabaseList CURSOR LOCAL FAST_FORWARD
FOR
SELECT DatabaseName
, DatabaseVersion
, DateChangedOn
FROM #Results
ORDER BY DateChangedOn;
OPEN DatabaseList;
WHILE 1 = 1
BEGIN
FETCH NEXT FROM DatabaseList INTO
#DatabaseName
, #DatabaseVersion
, #DateChangedOn;
IF ##FETCH_STATUS = -1 BREAK;
--process row here
END;
CLOSE DatabaseList;
DEALLOCATE DatabaseList;
You mention that the "std" tables exist in all databases. If these tables does not include system databases, change the IF statement in the script below to exclude those:
IF EXISTS (SELECT * FROM [?].sys.objects WHERE NAME = ''stdVersions'')
AND [?] NOT IN(N'master', N'model', N'tempdb', N'msdb', N'SSISDB')
I'm trying to identify the columns making up keys in ASE.
Sybase has the solution listed here: http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.help.ase.15.5/title.htm
I have a slightly modified version below, however it only works (just as sybase's solution) if I look up for a single table, but I want to use the 'in' keyword and look up all the tables in one shot.
Could I get some help, as to why the solution below does not work? It only generates the list of columns for 't5' table.
declare #keycnt integer
declare #objname varchar(256)
select #keycnt = keycnt, #objname = sysobjects.name from sysindexes, sysobjects
where
--sysobjects.id = object_id("t5")
--sysobjects.id = object_id("t4")
sysobjects.id in (object_id("t5"), object_id("t4"))
and sysobjects.id = sysindexes.id
and indid = 1
while #keycnt > 0
begin
select index_col(#objname, 1, #keycnt)
select #keycnt = #keycnt - 1
end
These are the tables I'm using for testing:
CREATE TABLE t4(
[value] [varchar] (500) not NULL ,
CONSTRAINT pk_g4 PRIMARY KEY CLUSTERED (
[value]
)
)
CREATE TABLE t5(
[myvalue] [varchar] (500) not NULL ,
CONSTRAINT pk_g4 PRIMARY KEY CLUSTERED (
[myvalue]
)
)
You have two solutions:
Using OR
declare #keycnt integer
declare #objname varchar(256)
select #keycnt = keycnt, #objname = sysobjects.name from sysindexes, sysobjects
where
--sysobjects.id = object_id("t5")
--sysobjects.id = object_id("t4")
(sysobjects.id = object_id("t5") OR sysobjects.id = object_id("t4"))
and sysobjects.id = sysindexes.id
and indid = 1
while #keycnt > 0
begin
select index_col(#objname, 1, #keycnt)
select #keycnt = #keycnt - 1
end
Or Using Dynamic SQL to properly use the IN.
I can't find an easy/generic way to register to an audit table the columns changed on some tables.
I tried to do it using a Trigger on after update in this way:
First of all the Audit Table definition:
CREATE TABLE [Audit](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Date] [datetime] NOT NULL default GETDATE(),
[IdTypeAudit] [int] NOT NULL, --2 for Modify
[UserName] [varchar](50) NULL,
[TableName] [varchar](50) NOT NULL,
[ColumnName] [varchar](50) NULL,
[OldData] [varchar](50) NULL,
[NewData] [varchar](50) NULL )
Next a trigger on AFTER UPDATE in any table:
DECLARE
#sql varchar(8000),
#col int,
#colcount int
select #colcount = count(*) from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'MyTable'
set #col = 1
while(#col < #colcount )
begin
set #sql=
'INSERT INTO Audit
SELECT 2, UserNameLastModif, ''MyTable'', COL_NAME(Object_id(''MyTable''), '+ convert(varchar,#col) +'), Deleted.'
+ COL_NAME(Object_id('MyTable'), #col) + ', Inserted.' + COL_NAME(Object_id('MyTable'), #col) + '
FROM Inserted LEFT JOIN Deleted ON Inserted.[MyTableId] = Deleted.[MyTableId]
WHERE COALESCE(Deleted.' + COL_NAME(Object_id('MyTable'), #col) + ', '''') <> COALESCE(Inserted.' + COL_NAME(Object_id('MyTable'), #col) + ', '''')'
--UserNameLastModif is an optional column on MyTable
exec(#sql)
set #col = #col + 1
end
The problems
Inserted and Deleted lost the context when I use the exec function
Seems that colnumber it isn't always a correlative number, seems if you create a table with 20 columns and you delete one and create another, the last one have a number > #colcount
I was looking for a solution for all over the net but I couln't figure out
Any Idea?
Thanks!
This highlights a greater problem with structural choice. Try to write a set-based solution. Remove the loop and dynamic SQL and write a single statement that inserts the Audit rows. It is possible but to make it easier consider a different table layout, like keeping all columns on 1 row instead of splitting them.
In SQL 2000 use syscolumns. In SQL 2005+ use sys.columns. i.e.
SELECT column_id FROM sys.columns WHERE object_id = OBJECT_ID(DB_NAME()+'.dbo.Table');
#Santiago : If you still want to write it in dynamic SQL, you should prepare all of the statements first then execute them.
8000 characters may not be enough for all the statements. A good solution is to use a table to store them.
IF NOT OBJECT_ID('tempdb..#stmt') IS NULL
DROP TABLE #stmt;
CREATE TABLE #stmt (ID int NOT NULL IDENTITY(1,1), SQL varchar(8000) NOT NULL);
Then replace the line exec(#sql) with INSERT INTO #stmt (SQL) VALUES (#sql);
Then exec each row.
WHILE EXISTS (SELECT TOP 1 * FROM #stmt)
BEGIN
BEGIN TRANSACTION;
EXEC (SELECT TOP 1 SQL FROM #stmt ORDER BY ID);
DELETE FROM #stmt WHERE ID = (SELECT MIN(ID) FROM #stmt);
COMMIT TRANSACTION;
END
Remember to use sys.columns for the column loop (I shall assume you use SQL 2005/2008).
SET #col = 0;
WHILE EXISTS (SELECT TOP 1 * FROM sys.columns WHERE object_id = OBJECT_ID('MyTable') AND column_id > #col)
BEGIN
SELECT TOP 1 #col = column_id FROM sys.columns
WHERE object_id = OBJECT_ID('MyTable') AND column_id > #col ORDER BY column_id ASC;
SET #sql ....
INSERT INTO #stmt ....
END
Remove line 4 #colcount int and the proceeding comma. Remove Information schema select.
DO not ever use any kind of looping a trigger. Do not use dynamic SQl or call a stored proc or send an email.All of these things are exretemly inappropriate in a trigger.
If tyou want to use dynamic sql use it to create the script to create the trigger. And create an audit table for every table you want audited (we actually have two for every table) or you will have performance problems due to locking on the "one table to rule them all".
i've got around 10 tables in my sql 2008 server.
Currently, my mdf is around 3.5Gig. (I also have some binary data in some of the tables). So, I'm wondering if there's a way i could see which tables are the biggest in size.
Is this possible?
Maybe it's an index or FTS catalog instead?
run this:
/******************************************************************************
** File: “GetTableSpaceUseage.sql”
** Name: Get Table Space Useage for a specific schema
** Auth: Robert C. Cain
** Date: 01/27/2008
**
** Desc: Calls the sp_spaceused proc for each table in a schema and returns
** the Table Name, Number of Rows, and space used for each table.
**
** Called by:
** n/a – As needed
**
** Input Parameters:
** In the code check the value of #schemaname, if you need it for a
** schema other than dbo be sure to change it.
**
** Output Parameters:
** NA
*******************************************************************************/
/*—————————————————————————*/
/* Drop the temp table if it's there from a previous run */
/*—————————————————————————*/
if object_id(N'tempdb..[#TableSizes]') is not null
drop table #TableSizes ;
go
/*—————————————————————————*/
/* Create the temp table */
/*—————————————————————————*/
create table #TableSizes
(
[Table Name] nvarchar(128) /* Name of the table */
, [Number of Rows] char(11) /* Number of rows existing in the table. */
, [Reserved Space] varchar(18) /* Reserved space for table. */
, [Data Space] varchar(18) /* Amount of space used by data in table. */
, [Index Size] varchar(18) /* Amount of space used by indexes in table. */
, [Unused Space] varchar(18) /* Amount of space reserved but not used. */
) ;
go
/*—————————————————————————*/
/* Load the temp table */
/*—————————————————————————*/
declare #schemaname varchar(256) ;
-- Make sure to set next line to the Schema name you want!
set #schemaname = 'dbo' ;
-- Create a cursor to cycle through the names of each table in the schema
declare curSchemaTable cursor
for select sys.schemas.name + '.' + sys.objects.name
from sys.objects
, sys.schemas
where object_id > 100
and sys.schemas.name = #schemaname
/* For a specific table uncomment next line and supply name */
--and sys.objects.name = 'specific-table-name-here'
and type_desc = 'USER_TABLE'
and sys.objects.schema_id = sys.schemas.schema_id ;
open curSchemaTable ;
declare #name varchar(256) ; /* This holds the name of the current table*/
-- Now loop thru the cursor, calling the sp_spaceused for each table
fetch curSchemaTable into #name ;
while ( ##FETCH_STATUS = 0 )
begin
insert into #TableSizes
exec sp_spaceused #objname = #name ;
fetch curSchemaTable into #name ;
end
/* Important to both close and deallocate! */
close curSchemaTable ;
deallocate curSchemaTable ;
/*—————————————————————————*/
/* Feed the results back */
/*—————————————————————————*/
select [Table Name]
, [Number of Rows]
, [Reserved Space]
, [Data Space]
, [Index Size]
, [Unused Space]
from [#TableSizes]
order by [Table Name] ;
/*—————————————————————————*/
/* Remove the temp table */
/*—————————————————————————*/
drop table #TableSizes ;
taken from Robert Caine blog
Edited the code to parse, several chars that were in single quote used a special single quote, as well the -- sign.
This code is for Microsoft SQL 2005+
exec sp_spaceused [tablename]
sys.allocations_units has the information you need. You join with sys.partitions to group all allocation units of a partition together and also to obtain the more usable object_id rather than the esoteric allocation_unit_id.
select object_name(p.object_id),
sum(au.total_pages)*8 as [space_in_kb]
from sys.partitions p
join sys.allocation_units au on p.hobt_id = au.container_id
group by p.object_id
order by [space_in_kb] desc
And yes, all tables (heap or clustered) are 'partitions', the terms does not refer to partitioned tables. sys.partitions also has the 'rows' column that may interest you.
exec sp_spaceused <tablename>
This query shows the size of each table in the current database.
SELECT sysobjects.[name] AS [TableName],
SUM(sysindexes.reserved) * 8 AS [Size(KB)],
SUM(sysindexes.dpages) * 8 AS [Data(KB)],
(SUM(sysindexes.used) - SUM(sysindexes.dpages)) * 8 AS [Indexes(KB)],
(SUM(sysindexes.reserved) - SUM(sysindexes.dpages)) * 8 AS [Unused(KB)]
FROM dbo.sysindexes AS sysindexes
JOIN dbo.sysobjects AS sysobjects ON sysobjects.id = sysindexes.id
WHERE sysobjects.[type] = 'U'
GROUP BY sysobjects.[name]
ORDER BY [Size(KB)] DESC
In SQL 2008+: right-click on the DB name in SSMS, select Reports, then Standard Reports, then Disk Usage by Table.
Sometimes I run this... It gets all tables to temp table, loops it through and gets sizes for all tables. Result data is in #tablesizes, so you can query it how you like.
Works in Sql Server >2005
declare #tables TABLE
(
table_name nvarchar(200)
)
declare #tablesizes TABLE
(
[name] nvarchar(200),
[rows] int,
reserved nvarchar(200),
data nvarchar(200),
index_size nvarchar(200),
unused nvarchar(200),
reserved_int int,
data_int int,
index_size_int int,
unused_int int
)
declare #t nvarchar(200)
insert into #tables
select Table_Name from information_schema.tables
while exists(select * from #tables)
begin
set #t=(select top 1 table_name from #tables)
insert into #tablesizes([name],[rows],reserved,data,index_size,unused)
exec sp_spaceused #t
delete top (1) from #tables
end
update #tablesizes set
reserved_int=convert(int, replace(reserved,' KB','')),
data_int=convert(int, replace(data,' KB','')),
index_size_int=convert(int, replace(index_size,' KB','')),
unused_int=convert(int, replace(unused,' KB',''))
select * from #tablesizes order by data_int desc
You could use:
SELECT ##servername;
IF EXISTS(SELECT name FROM tempdb.sys.tables WHERE name LIKE '#spaceUsed%')
BEGIN
DROP TABLE #spaceUsed;
END;
CREATE TABLE #spaceUsed (
name VARCHAR(255) ,
rows INT ,
reserved VARCHAR(50) ,
data VARCHAR(50) ,
index_size VARCHAR(50) ,
unused VARCHAR(50));
EXEC sp_msforeachtable
#command1 ='
--
INSERT INTO #spaceUsed
exec sp_spaceused N''?'';
'
,#whereand = ' And Object_id In (Select Object_id From sys.objects
Where SCHEMA_NAME(Schema_ID) like ''%'')';
DECLARE
#spaceUsedData TABLE (
name VARCHAR(255) ,
rows INT ,
reservedMB BIGINT NULL ,
dataMB BIGINT NULL ,
index_sizeMB BIGINT NULL ,
unusedMB BIGINT NULL);
INSERT INTO INTO #spaceUsedData (name , rows , reservedMB , dataMB ,index_sizeMB ,unusedMB)
SELECT name , rows ,
Convert ( BIGINT ,Ltrim(Rtrim(Replace(reserved ,'KB' ,'')) ))/1024 ,
Convert ( BIGINT ,Ltrim(Rtrim(Replace(data ,'KB' ,'')) ))/1024 ,
Convert ( BIGINT ,Ltrim(Rtrim(Replace(index_size ,'KB' ,'')) ))/1024 ,
Convert ( BIGINT ,Ltrim(Rtrim(Replace(unused ,'KB' ,'')) ))/1024
FROM #spaceUsed;
SELECT * , reservedMB+ dataMB+index_sizeMB+unusedMB AS TotalMB FROM #spaceUsedData
ORDER BY rows DESC;