se tem uma coisa que gosto de linguagens, programas ou mesmo tecnologias mais novas, é que elas simples o que realmente devia ser simples desde o começo. Vários são os exemplos, mas o que quero citar agora, é o do MySQL. Quando quero saber quais são os bancos de dados ou schemas do meu servidor, basta digitar um simples comando:
SHOWDATABASES;
SHOW DATABASES;
Simples, direto, semântico. Praticamente não é preciso pensar para digitar esse comando. Mas, quando se está no SQL Server, que ultimamente tem focado quase que totalmente em ferramentas visuais, obter uma lista dessa não é impossível, mas faz você parar para pensar demais, ou até mesmo pesquisar na internet por causa disso.
Bom, se você chegou aqui por causa disso, abaixo vão alguns meios de obter a lista de banco de dados do seu servidor usando algumas linhas de T-SQL:
----SQL SERVER 2005 System ProceduresEXEC sp_databases
EXEC sp_helpdb
----SQL 2000 Method still works in SQL Server 2005SELECT name
FROM sys.databases
SELECT name
FROM sys.sysdatabases
----SQL SERVER Un-Documented ProcedureEXEC sp_msForEachDB 'PRINT ''?'''
----SQL SERVER 2005 System Procedures
EXEC sp_databases
EXEC sp_helpdb
----SQL 2000 Method still works in SQL Server 2005
SELECT name
FROM sys.databases
SELECT name
FROM sys.sysdatabases
----SQL SERVER Un-Documented Procedure
EXEC sp_msForEachDB 'PRINT ''?'''
O créditos pelo código vão para o colega Pinal Dave.
Como mais uma necessidade “doida” do dia a dia que vivemos, foi preciso procurar em todas as tabelas de um banco de dados SQL Server que continham uma determinada string. Pois bem, com várias opções em mãos, a menos complicada de enviar para um ambiente de produção sem a necessidade de um Application Server era uma query SQL que fizesse o trabalho.
Depois de muito suor e testes, o resultado foi a query abaixo:
DECLARE @SQLVARCHAR(8000)DECLARE @filtro VARCHAR(200)DECLARE @filtro_www VARCHAR(200)-- inicia a declaração do sqlSET @SQL=''SET @filtro ='%texto_procurado%'SELECT
tabelas.name AS Tabela
,colunas.name AS Coluna
,tipos.name AS Tipo
,colunas.length AS Tamanho
INTO
#result
FROM
sysobjects tabelas
INNERJOIN syscolumns colunas
ON colunas.id = tabelas.id
--INNERJOIN systypes tipos
ON tipos.xtype = colunas.xtype
WHERE
tabelas.xtype ='u'AND-- colocar aqui os tipos de coluna que serão buscados
tipos.name IN('text','ntext','varchar','nvarchar')-- cursor para varrer as tabelasDECLARE cTabelas cursor LOCAL fast_forward FORSELECTDISTINCT Tabela FROM #result
DECLARE @nomeTabela VARCHAR(255)OPEN cTabelas
fetch NEXTFROM cTabelas INTO @nomeTabela
while @@fetch_status =0BEGIN-- cursor para varrer as colunas da tabela correnteDECLARE cColunas cursor LOCAL fast_forward FORSELECT Coluna, Tipo, Tamanho FROM #result WHERE Tabela = @nomeTabela
DECLARE @nomeColuna VARCHAR(255)DECLARE @tipoColuna VARCHAR(255)DECLARE @tamanhoColuna VARCHAR(255)OPEN cColunas
-- monta as colunas da cláusula select
fetch NEXTFROM cColunas INTO @nomeColuna, @tipoColuna, @tamanhoColuna
while @@fetch_status =0BEGIN-- cria a declaração da variávelSET @SQL='declare @hasresults bit'+CHAR(13)+CHAR(10)+CHAR(13)+CHAR(10)-- cria o selectSET @SQL= @SQL+'select'+CHAR(13)+CHAR(10)SET @SQL= @SQL+CHAR(9)+''''+ @nomeTabela +''' AS NomeTabela'SET @SQL= @SQL+CHAR(9)+','+ @nomeColuna +CHAR(13)+CHAR(10)-- adiciona uma coluna com o tipo e o tamanho do campoSET @SQL= @SQL+CHAR(9)+','+''''+ @tipoColuna +''' AS '''+ @nomeColuna +'_Tipo'''+CHAR(13)+CHAR(10)SET @SQL= @SQL+CHAR(9)+','+'DATALENGTH('+ @nomeColuna +') AS '''+ @nomeColuna +'_Tamanho_Ocupado'''+CHAR(13)+CHAR(10)SET @SQL= @SQL+CHAR(9)+','+''''+ @tamanhoColuna +''' AS '''+ @nomeColuna +'_Tamanho_Maximo'''+CHAR(13)+CHAR(10)-- define a tabela temporária (#result)SET @SQL= @SQL+'into'+CHAR(13)+CHAR(10)+CHAR(9)+'#result_'+ @nomeTabela +CHAR(13)+CHAR(10)-- adiciona a cláusula fromSET @SQL= @SQL+'from'+CHAR(13)+CHAR(10)+CHAR(9)+ @nomeTabela +CHAR(13)+CHAR(10)-- inicia a montagem do whereSET @SQL= @SQL+'where'+CHAR(13)+CHAR(10)SET @SQL= @SQL+CHAR(9)+ @nomeColuna +' like '''+ @filtro +''''+CHAR(13)+CHAR(10)SET @SQL= @SQL+CHAR(13)+CHAR(10)+'select @hasresults = count(*) from #result_'+ @nomeTabela +CHAR(13)+CHAR(10)SET @SQL= @SQL+CHAR(13)+CHAR(10)+'if @hasresults > 0'SET @SQL= @SQL+CHAR(13)+CHAR(10)+'begin'SET @SQL= @SQL+CHAR(13)+CHAR(10)+CHAR(9)+'select * from #result_'+ @nomeTabela
SET @SQL= @SQL+CHAR(13)+CHAR(10)+'end'+CHAR(13)+CHAR(10)SET @SQL= @SQL+CHAR(13)+CHAR(10)+'drop table #result_'+ @nomeTabela
SET @SQL= @SQL+CHAR(13)+CHAR(10)
fetch NEXTFROM cColunas INTO @nomeColuna, @tipoColuna, @tamanhoColuna
-- descomente a linha abaixo para ver o SQL produzido no janela de Messages-- print @sqlEXEC(@SQL)SET @SQL=''END
close cColunas
deallocate cColunas
fetch NEXTFROM cTabelas INTO @nomeTabela
END
close cTabelas
deallocate cTabelas
DROPTABLE #result
declare @sql varchar(8000)
declare @filtro varchar(200)
declare @filtro_www varchar(200)
-- inicia a declaração do sql
set @sql = ''
set @filtro = '%texto_procurado%'
select
tabelas.name as Tabela
,colunas.name as Coluna
,tipos.name as Tipo
,colunas.length as Tamanho
into
#result
from
sysobjects tabelas
inner join syscolumns colunas
on colunas.id = tabelas.id
--
inner join systypes tipos
on tipos.xtype = colunas.xtype
where
tabelas.xtype = 'u'
and
-- colocar aqui os tipos de coluna que serão buscados
tipos.name in('text', 'ntext', 'varchar', 'nvarchar')
-- cursor para varrer as tabelas
declare cTabelas cursor local fast_forward for
select distinct Tabela from #result
declare @nomeTabela varchar(255)
open cTabelas
fetch next from cTabelas into @nomeTabela
while @@fetch_status = 0
begin
-- cursor para varrer as colunas da tabela corrente
declare cColunas cursor local fast_forward for
select Coluna, Tipo, Tamanho from #result where Tabela = @nomeTabela
declare @nomeColuna varchar(255)
declare @tipoColuna varchar(255)
declare @tamanhoColuna varchar(255)
open cColunas
-- monta as colunas da cláusula select
fetch next from cColunas into @nomeColuna, @tipoColuna, @tamanhoColuna
while @@fetch_status = 0
begin
-- cria a declaração da variável
set @sql = 'declare @hasresults bit' + char(13) + char(10) + char(13) + char(10)
-- cria o select
set @sql = @sql + 'select' + char(13) + char(10)
set @sql = @sql + char(9) + '''' + @nomeTabela + ''' AS NomeTabela'
set @sql = @sql + char(9) + ',' + @nomeColuna + char(13) + char(10)
-- adiciona uma coluna com o tipo e o tamanho do campo
set @sql = @sql + char(9) + ',' + '''' + @tipoColuna + ''' AS ''' + @nomeColuna + '_Tipo''' + char(13) + char(10)
set @sql = @sql + char(9) + ',' + 'DATALENGTH(' + @nomeColuna + ') AS ''' + @nomeColuna + '_Tamanho_Ocupado''' + char(13) + char(10)
set @sql = @sql + char(9) + ',' + '''' + @tamanhoColuna + ''' AS ''' + @nomeColuna + '_Tamanho_Maximo''' + char(13) + char(10)
-- define a tabela temporária (#result)
set @sql = @sql + 'into' + char(13) + char(10) + char(9) + '#result_' + @nomeTabela + char(13) + char(10)
-- adiciona a cláusula from
set @sql = @sql + 'from' + char(13) + char(10) + char(9) + @nomeTabela + char(13) + char(10)
-- inicia a montagem do where
set @sql = @sql + 'where' + char(13) + char(10)
set @sql = @sql + char(9) + @nomeColuna + ' like ''' + @filtro + '''' + char(13) + char(10)
set @sql = @sql + char(13) + char(10) + 'select @hasresults = count(*) from #result_' + @nomeTabela + char(13) + char(10)
set @sql = @sql + char(13) + char(10) + 'if @hasresults > 0'
set @sql = @sql + char(13) + char(10) + 'begin'
set @sql = @sql + char(13) + char(10) + char(9) + 'select * from #result_' + @nomeTabela
set @sql = @sql + char(13) + char(10) + 'end' + char(13) + char(10)
set @sql = @sql + char(13) + char(10) + 'drop table #result_' + @nomeTabela
set @sql = @sql + char(13) + char(10)
fetch next from cColunas into @nomeColuna, @tipoColuna, @tamanhoColuna
-- descomente a linha abaixo para ver o SQL produzido no janela de Messages
-- print @sql
exec(@sql)
set @sql = ''
end
close cColunas
deallocate cColunas
fetch next from cTabelas into @nomeTabela
end
close cTabelas
deallocate cTabelas
drop table #result
O que ela faz exatamente?
Busca no sysobjects todos as tabelas existentes no banco de dados.
Faz o mesmo na tabela syscolumns para obter o nome da coluna, o tamanho e o tipo delas, conforme configuração
Por fim, verifica se o texto existe na tabela e campo informado
Se houver, apresente um resultset completo com o nome da tabela, o dado, o tipo do campo e tamanho
A query foi testada no SQL Server 2000 e 2005, funcionando perfeitamente e ambos o casos.
É importante notar que não foram colocadas aqui considerações de desempenho ou o uso do recurso fulltext, sendo o objetivo principal resolver o problema de encontrar o texto desejado.
Não é muito comum, mas o ADO.NET nos dá a possibilidade de através de uma consulta feita pelo objeto Command (ou DataAdapter), retornar mais de um ResultSet ou tabela do banco de dados.
Em um exemplo, veja o código abaixo
1
2
3
4
5
6
7
8
9
10
11
stringselect="select * from Categories; select * from Customers";
SqlCommand command =new SqlCommand (select, conn );
SqlDataAdapter adapter =new SqlDataAdapter();
adapter.SelectCommand= command;
DataSet ds =new DataSet();
adapter.Fill(ds);foreach(DataTable table in ds.Tables){
Console.WriteLine(table.TableName);}
string select = "select * from Categories; select * from Customers";
SqlCommand command = new SqlCommand ( select, conn );
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
DataSet ds = new DataSet();
adapter.Fill(ds);
foreach(DataTable table in ds.Tables)
{
Console.WriteLine(table.TableName);
}
Naturalmente, o comando sql podia chamar uma stored procedure existente no banco de dados, o que daria o mesmo efeito. Com isso, o DataSet ds teria como resultado 2 objetos DataTable preenchidos, Categories e Customers, resultado do comando sql disparado.
Mas, como conseguir esse efeito com um DataReader, sendo que comumente sempre esperamos um ResultSet, iterando através dos campos diretamente pelo índice ou nome deste? Simples! A interface IDataReader possui o método NextResult que permite pular para o próximo ResultSet, caso este exista. O NextResult retorna um bool, que da mesma forma que o método Read, retorna true se houver mais resultados e automaticamente “move o cursor” para o próximo ResultSet. O exemplo abaixo demonstra o funcionamento:
Apesar do SQL Management Studio Express ser uma ferramenta poderosa e gratuita, peca em algumas funcionalidades simples que a maioria das ferramentas free tem. A título de exemplo, as ferramentas do MySQL como o Administrator e o Query Browser são extremente completas, auxiliando inclusive em uma funcionalidade que coloque em questão aqui. A geração de scripts de dados de um banco.
Tá, conseguimos facilmente geraro script de criação das tabelas, procs, enfim, tudo mais de objetos existentes no banco através SQL Management Studio Express, clicando com o botão direto do mouse sobre o database na janela Object Explorer, indo em Tasks, Generate Scripts… Daí, basta selecionar os objetos a serem gerados e voilá! Está tudo aí, pronto para ser salvo e criado em qualquer outra servidor.
O problema são os dados. Tudo bem, dá até pra fazer um backup em arquivo binário, mas e se o servidor for de acesso restrito? A rotina de backup só funciona apontando para os discos existentes no computador instalado. Com algum esforço, daria para apontar para um diretório da rede, mas nem sempre isso está acessível.
Portanto, para auxiliar nesta tarefa, o colega Mark Clerget publicou um seu blog uma query muito bacana que é capaz de gerar os inserts dos dados das tabelas de um determinado banco. Dê uma olhada no script abaixo:
CREATETABLE #tmp (
SQLText VARCHAR(8000))CREATETABLE #tmp2 (
Id INTIDENTITY,
SQLText VARCHAR(8000))SET nocount ONDELETE #tmp
DELETE #tmp2
DECLARE @vsSQL VARCHAR(8000),
@vsCols VARCHAR(8000),
@vsTableName VARCHAR(40)DECLARE csrTables cursor FORSELECT name
FROM sysobjects
WHERETYPEIN('u')AND name IN('Customers')ORDERBY name
OPEN csrTables
fetch NEXTFROM csrTables INTO @vsTableName
while (@@fetch_status =0)BEGINSELECT @vsSQL ='',
@vsCols =''SELECT @vsSQL = @vsSQL +CASEWHEN sc.type IN(39,47,61,111)THEN'''''''''+'+'isnull(rtrim(replace('+ sc.name +','''''''','''''''''''')),'''')'+'+'''''',''+'WHEN sc.type =35THEN'''''''''+'+'isnull(rtrim(replace(substring('+ sc.name +',1,1000),'''''''','''''''''''')),'''')'+'+'''''',''+'ELSE'isnull(convert(varchar,'+ sc.name +'),''null'')+'',''+'ENDFROM syscolumns sc
WHERE sc.id = object_id(@vsTableName)ORDERBY ColID
SELECT @vsCols = @vsCols + sc.name +','FROM syscolumns sc
WHERE sc.id = object_id(@vsTableName)ORDERBY ColID
SELECT @vsSQL =SUBSTRING(@vsSQL,1,datalength(@vsSQL)-1)SELECT @vsCols =SUBSTRING(@vsCols,1,datalength(@vsCols)-1)INSERT #tmp
EXEC('select '+ @vsSQL +' from '+ @vsTableName)UPDATE #tmp
SET sqltext ='insert '+ @vsTableName +'('+ @vsCols +') values('+SUBSTRING(sqltext,1,datalength(sqltext)-1)+')'INSERT #tmp2
SELECT'DELETE from '+ @vsTableName
INSERT #tmp2 VALUES('GO')IF(SELECTCOUNT(id)FROM syscolumns WHERE id = object_id(@vsTableName)AND((STATUS & 128)=128))=1BEGININSERT #tmp2
SELECT'set identity_insert '+ @vsTableName +' on'ENDINSERT #tmp2
SELECT*FROM #tmp
IF(SELECTCOUNT(id)FROM syscolumns WHERE id = object_id(@vsTableName)AND((STATUS & 128)=128))=1BEGININSERT #tmp2
SELECT'set identity_insert '+ @vsTableName +' off'ENDINSERT #tmp2 VALUES('GO')INSERT #tmp2
SELECT'update statistics '+ @vsTableName
INSERT #tmp2 VALUES('GO')DELETE #tmp
fetch NEXTFROM csrTables INTO @vsTableName
END
close csrTables
deallocate csrTables
UPDATE #tmp2
SET sqltext =SUBSTRING(sqltext,1,charindex(',)',sqltext)-1)+',NULL)'WHERENOT(charindex(',)',sqltext)=0)UPDATE #tmp2
SET sqltext =REPLACE(sqltext,',''''',',null')WHERENOT(charindex(',''''',sqltext)=0)UPDATE #tmp2
SET sqltext =REPLACE(sqltext,'(''''',',null')WHERENOT(charindex('(''''',sqltext)=0)SET nocount off
SELECT sqltext FROM #tmp2 ORDERBY id
GODROPTABLE #tmp
DROPTABLE #tmp2
create table #tmp (
SQLText varchar(8000) )
create table #tmp2 (
Id int identity,
SQLText varchar(8000) )
set nocount on
delete #tmp
delete #tmp2
declare @vsSQL varchar(8000),
@vsCols varchar(8000),
@vsTableName varchar(40)
declare csrTables cursor for
select name
from sysobjects
where type in ('u')
and name in ('Customers')
order by name
open csrTables
fetch next from csrTables into @vsTableName
while (@@fetch_status = 0)
begin
select @vsSQL = '',
@vsCols = ''
select @vsSQL = @vsSQL +
CASE when sc.type in (39,47,61,111) then
'''''''''+' + 'isnull(rtrim(replace('+ sc.name + ','''''''','''''''''''')),'''')' + '+'''''',''+'
when sc.type = 35 then
'''''''''+' + 'isnull(rtrim(replace(substring('+ sc.name + ',1,1000),'''''''','''''''''''')),'''')' + '+'''''',''+'
else
'isnull(convert(varchar,' + sc.name + '),''null'')+'',''+'
end
from syscolumns sc
where sc.id = object_id(@vsTableName)
order by ColID
select @vsCols = @vsCols + sc.name + ','
from syscolumns sc
where sc.id = object_id(@vsTableName)
order by ColID
select @vsSQL = substring(@vsSQL,1,datalength(@vsSQL)-1)
select @vsCols = substring(@vsCols,1,datalength(@vsCols)-1)
insert #tmp
exec ('select ' + @vsSQL + ' from ' + @vsTableName)
update #tmp
set sqltext = 'insert ' + @vsTableName + '(' + @vsCols + ') values(' + substring(sqltext,1,datalength(sqltext)-1) + ')'
insert #tmp2
select 'DELETE from ' + @vsTableName
insert #tmp2 values ('GO')
if (select count(id) from syscolumns where id = object_id(@vsTableName) and ((status & 128) = 128) ) = 1
begin
insert #tmp2
select 'set identity_insert ' + @vsTableName + ' on'
end
insert #tmp2
select * from #tmp
if (select count(id) from syscolumns where id = object_id(@vsTableName) and ((status & 128) = 128) ) = 1
begin
insert #tmp2
select 'set identity_insert ' + @vsTableName + ' off'
end
insert #tmp2 values ('GO')
insert #tmp2
select 'update statistics ' + @vsTableName
insert #tmp2 values ('GO')
delete #tmp
fetch next from csrTables into @vsTableName
end
close csrTables
deallocate csrTables
update #tmp2
set sqltext = substring(sqltext,1,charindex(',)',sqltext)-1) + ',NULL)'
where not(charindex(',)',sqltext) = 0)
update #tmp2
set sqltext = replace(sqltext, ',''''',',null')
where not (charindex(',''''',sqltext) = 0)
update #tmp2
set sqltext = replace(sqltext, '(''''',',null')
where not (charindex('(''''',sqltext) = 0)
set nocount off
select sqltext from #tmp2 order by id
go
drop table #tmp
drop table #tmp2
Atentem à linha com o código “and name in ('Customers')” pois é este que faz o filtro nas tabelas contidas em sysobjects.. Se quiser apenas um subconjunto das tabelas nas quais terão o script gerado, basta alterar este filtro. O resultado da query acima executada sobre o banco de dados Northwind na tabela Region, pode ser visto na figura abaixo:
É um belo macete para as horas de aperto. Porém, este script não é infalível. Para alguns tipos de campos, como o image, ainda são gerados alguns erros. Mas como eles são excessão, é possível tratá-los de maneira separada. Caso alguém tenha alguma sugestão de melhoria, é avisar!