when I do:
SELECT *
FROM SOMETABLE
I get all the columns from SOMETABLE, but I DON'T want the columns which are NULL (for all records). How do I do this?
Reason: this table has 20 columns, 10 of these are set but 10 of them are null for certain queries. And it is time consuming to type the columnnames....
Thanks,
Voodoo
SQL supports the * wildcard which means all columns. There is no wildcard for all columns except the ones you don't want.
Type out the column names. It can't be more work than asking questions on Stack Overflow. Also, copy & paste is your friend.
Another suggestion is to define a view that selects the columns you want, and then subsequently you can select * from the view any time you want.
It's possible to do, but kind of complicated. You can retrieve the list of columns in a table from INFORMATION_SCHEMA.COLUMNS. For each column, you can run a query to see if any non-null row exists. Finally, you can run a query based on the resulting column list.
Here's one way to do that, with a cursor:
declare #table_name varchar(256)
set #table_name = 'Airports'
declare #rc int
declare #query nvarchar(max)
declare #column_list varchar(256)
declare columns cursor local for select column_name
from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = #table_name
open columns
declare #column_name varchar(256)
fetch next from columns into #column_name
while ##FETCH_STATUS = 0
begin
set #query = 'select #rc = count(*) from ' + #table_name + ' where ' +
#column_name + ' is not null'
exec sp_executesql #query = #query, #params = N'#rc int output',
#rc = #rc output
if #rc > 0
set #column_list = case when #column_list is null then '' else
#column_list + ', ' end + #column_name
fetch next from columns into #column_name
end
close columns
deallocate columns
set #query = 'select ' + #column_list + ' from ' + #table_name
exec sp_executesql #query = #query
This runs on SQL Server. It might be close enough for Sybase. Hopefully, this demonstrates that typing out a column list isn't that bad :-)
Related
I'm using MySQL and we use a Deleted column to do "soft" deletes and keep history. I'm trying to write either a procedure or SQL statement which can get me the count from each table.
I believe this should be possible, as it somewhat reminds me of a SQL injection attack I've seen before. The difference is I want to use this for good purposes :)
I reworked the code shown there, but think my syntax may be off for MySQL; how can I correct this, or should I find a better way of accomplishing this? Below is reworked from the link (so pretty sure it doesn't follow the MySQL procedure syntax).
Here is what I have so far:
Set ansi_warnings off
Declare #T VARCHAR(255)
DECLARE Table_Cursor CURSOR FOR
Select
c.TABLE_NAME
from INFORMATION_SCHEMA.columns c, INFORMATION_SCHEMA.tables t
where
t.table_name = c.table_name
and t.table_type = 'BASE TABLE'
AND c.COLUMN_NAME = 'Deleted'
OPEN Table_Cursor
FETCH NEXT FROM Table_Cursor INTO #T
WHILE(##FETCH_STATUS=0)
BEGIN
EXEC ( 'SELECT COUNT(*) AS ' + #T + 'Count FROM ' + #T + ' WHERE Deleted IS NULL' );
FETCH NEXT FROM Table_Cursor INTO #T
END
CLOSE Table_Cursor
DEALLOCATE Table_Cursor
I would guess that helps. Instead of
BEGIN
EXEC ( 'SELECT COUNT(*) AS ' + #T + 'Count FROM ' + #T + ' WHERE Deleted IS NULL' );
# would you not store that result somewhere?
FETCH NEXT FROM Table_Cursor INTO #T
END
just try
BEGIN
EXEC ( 'SELECT "#T" as tabname, COUNT(*) AS del_Count FROM ' + #T + ' WHERE Deleted IS NULL' );
# somewhere store the number and the tabname
FETCH NEXT FROM Table_Cursor INTO #T
END
I have an issue wen I try to output a value from a dynamic T-SQL Query inside of a stored procedure.
I try to execute the following and simply output a 1 if something was found:
SET NOCOUNT ON
DECLARE #returnValue int
DECLARE #Statement nvarchar(400)
set #Statement = 'SELECT #result=''1'' FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = ''sourceTable''
AND COLUMN_NAME = #columnIN
AND TABLE_SCHEMA = ''dbo'''
exec sp_executesql #Statement,
N'#columnIN nvarchar(60),#result INT OUTPUT',
#columnIN = #column, #result=#returnValue OUTPUT
select #returnValue
This currently yields NULL. Does anyone have any idea what I'm missing?
Additional Information:
The column that I try to lookup is for example column1 . If I run the SQL query with ...AND CLOUMN_NAME = 'column1' ... I get a 1 back.
If I print the #column variable in the SP I get 'column1'.
#column is declared as an input variable with nvarchar(60) in the SP: PROCEDURE [dbo].[usp_checkColumn] (#column nvarchar(60), #result INT OUTPUT)
As per request the complete SP here:
Create PROCEDURE [dbo].[usp_checkColumn] (#column nvarchar(60), #result INT OUTPUT)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
DECLARE #returnValue int
DECLARE #Statement nvarchar(400)
set #Statement = 'SELECT #result=''1'' FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = ''t_table''
AND COLUMN_NAME = #columnIN
AND TABLE_SCHEMA = ''dbo'''
exec sp_executesql #Statement, N'#columnIN nvarchar(60),#result INT OUTPUT', #columnIN = #column, #result=#returnValue OUTPUT
select #returnValue
return #returnValue
END
And here's how I call the SP:
DECLARE fcursor CURSOR
FOR
select FieldName
from t_fieldDefinition
OPEN fcursor
Fetch next from fcursor into #field;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #tmpField = '''' + #field + ''''
SET #field ='[' + #field + ']'
set #available = 0
exec usp_checkColumn #tmpField,#available OUTPUT
If I print the [#column] variable in the usp_checkColumn I do get the correct column inside of the ''. If I copy & paste the print of this variable and insert it into the query I get a 1 back, if I run the SP I get NULL(converted to 0 as NULL is not valid for the INT variable) back.
Here's the content of the t_fieldDefinition table:
FieldName ID
Source 5
column1 6
column2 7
Client 8
asd BSX 9
bsd 10
esd 11
esx 12
And here's the definition of the t_table table:
ID bigint Unchecked
Source varchar(250) Checked
column1 varchar(250) Checked
column2 nvarchar(100) Checked
Client varchar(10) Checked
asd varchar(250) Checked
[asd BSX] varchar(250) Checked
so that means that it should return 1 for all that are inside of the table definition and 0 for all others. Is it possible that the fields with a white space can be the issue? Although they work as well when you do it manually. It's not that I really have an option to change it but at least I would now the cause of the issue then.
I'm going to venture an answer on this.
I do not recommend using a cursor to go through each column just to test if the column exists in another table. If you insist on using a cursor, you could use a subquery to limit the results that you're cycling through to only the columns that don't exist in the other table, eliminating the need for the stored procedure that checks each individually (which by the way I think makes more sense as a user function):
DECLARE fcursor CURSOR
FOR
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
where table_name = 't_table' and not column_name in
(
SELECT COLUMN_Name FROM INFORMATION_SCHEMA.COLUMNS
where table_name = 't_fieldDefinition'
)
If your goal really is to alter the table to add any missing columns, then this can be improved by eliminating the cursor and outputting your results using FOR XML:
declare #sql varchar(max)
set #sql =
(
SELECT cast('ALTER TABLE t_fieldDefinition ADD [' + COLUMN_NAME + '] INT;' as varchar(max)) FROM INFORMATION_SCHEMA.COLUMNS
where table_name = 't_table' and not column_name in
(
SELECT COLUMN_Name FROM INFORMATION_SCHEMA.COLUMNS
where table_name = 't_fieldDefinition'
) FOR XML PATH ('')
)
print #sql
exec (#sql)
Is there a way I can convert a SQL Server 2008 Table to HTML table text, without knowing the structure of the table first?
I tried this:
USE [Altiris]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spCustomTable2HTML] (
#TABLENAME NVARCHAR(500),
#OUTPUT NVARCHAR(MAX) OUTPUT,
#TBL_STYLE NVARCHAR(1024) = '',
#HDR_STYLE NVARCHAR(1024) = '')
AS
-- #exec_str stores the dynamic SQL Query
-- #ParmDefinition stores the parameter definition for the dynamic SQL
DECLARE #exec_str NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(500)
--We need to use Dynamic SQL at this point so we can expand the input table name parameter
SET #exec_str= N'
DECLARE #exec_str NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(500)
--Make a copy of the original table adding an indexing columnWe need to add an index column to the table to facilitate sorting so we can maintain the
--original table order as we iterate through adding HTML tags to the table fields.
--New column called CustColHTML_ID (unlikely to be used by someone else!)
--
select CustColHTML_ID=0,* INTO #CustomTable2HTML FROM ' + #TABLENAME + '
--Now alter the table to add the auto-incrementing index. This will facilitate row finding
DECLARE #COUNTER INT
SET #COUNTER=0
UPDATE #CustomTable2HTML SET #COUNTER = CustColHTML_ID=#COUNTER+1
-- #HTMLROWS will store all the rows in HTML format
-- #ROW will store each HTML row as fields on each row are iterated through
-- using dymamic SQL and a cursor
-- #FIELDS will store the header row for the HTML Table
DECLARE #HTMLROWS NVARCHAR(MAX) DECLARE #FIELDS NVARCHAR(MAX)
SET #HTMLROWS='''' DECLARE #ROW NVARCHAR(MAX)
-- Create the first HTML row for the table (the table header). Ignore our indexing column!
SET #FIELDS=''<tr ' + #HDR_STYLE + '>''
SELECT #FIELDS=COALESCE(#FIELDS, '' '','''')+''<td>'' + name + ''</td>''
FROM tempdb.sys.Columns
WHERE object_id=object_id(''tempdb..#CustomTable2HTML'')
AND name not like ''CustColHTML_ID''
SET #FIELDS=#FIELDS + ''</tr>''
-- #ColumnName stores the column name as found by the table cursor
-- #maxrows is a count of the rows in the table, and #rownum is for marking the
-- ''current'' row whilst processing
DECLARE #ColumnName NVARCHAR(500)
DECLARE #maxrows INT
DECLARE #rownum INT
--Find row count of our temporary table
SELECT #maxrows=count(*) FROM #CustomTable2HTML
--Create a cursor which will look through all the column names specified in the temporary table
--but exclude the index column we added (CustColHTML_ID)
DECLARE col CURSOR FOR
SELECT name FROM tempdb.sys.Columns
WHERE object_id=object_id(''tempdb..#CustomTable2HTML'')
AND name not like ''CustColHTML_ID''
ORDER BY column_id ASC
--For each row, generate dymanic SQL which requests the each column name in turn by
--iterating through a cursor
SET #rowNum=0
SET #ParmDefinition=N''#ROWOUT NVARCHAR(MAX) OUTPUT,#rowNum_IN INT''
While #rowNum < #maxrows
BEGIN
SET #HTMLROWS=#HTMLROWS + ''<tr>''
SET #rowNum=#rowNum +1
OPEN col
FETCH NEXT FROM col INTO #ColumnName
WHILE ##FETCH_STATUS=0
BEGIN
--Get nth row from table
--SET #exec_str=''SELECT #ROWOUT=(select top 1 ['' + #ColumnName + ''] from (select top '' + cast(#rownum as varchar) + '' * from #CustomTable2HTML order by CustColHTML_ID ASC) xxx order by CustColHTML_ID DESC)''
SET #exec_str=''SELECT #ROWOUT=(select ['' + #ColumnName + ''] from #CustomTable2HTML where CustColHTML_ID=#rowNum_IN)''
EXEC sp_executesql
#exec_str,
#ParmDefinition,
#ROWOUT=#ROW OUTPUT,
#rowNum_IN=#rownum
SET #HTMLROWS =#HTMLROWS + ''<td>'' + #ROW + ''</td>''
FETCH NEXT FROM col INTO #ColumnName
END
CLOSE col
SET #HTMLROWS=#HTMLROWS + ''</tr>''
END
SET #OUTPUT=''''
IF #maxrows>0
SET #OUTPUT= ''<table ' + #TBL_STYLE + '>'' + #FIELDS + #HTMLROWS + ''</table>''
DEALLOCATE col
'
DECLARE #ParamDefinition nvarchar(max)
SET #ParamDefinition=N'#OUTPUT NVARCHAR(MAX) OUTPUT'
--Execute Dynamic SQL. HTML table is stored in #OUTPUT which is passed back up (as it's
--a parameter to this SP)
EXEC sp_executesql #exec_str,
#ParamDefinition,
#OUTPUT=#OUTPUT OUTPUT
RETURN 1
but when I execute the procedure
DECLARE #HTML NVARCHAR(MAX)
EXEC SpCustomTable2HTML 'Users', #HTML OUTPUT
SELECT #HTML
it keeps returning null.
Any ideas?
This SQL Fiddle DEMO shows your problem. When ALL the columns in ALL rows have values, you get a proper HTML table. When ANY NULLs exist, it turns the entire thing into NULL because
NULL + <any> = NULL
To fix it, simply change line 90 to handle nulls, i.e.
SET #HTMLROWS =#HTMLROWS + '''' + ISNULL(#ROW,'''') + ''''
The fixed SQL Fiddle DEMO
I realise it's been a while (to say the least) since this question was active but I thought I would post a few comments on this thread, as it turned up in a recent search.
Apologies if this (unintentionally) annoys the question asker but I believe the approach being used is both inefficient and difficult to understand - and therefore maintain.
There's no need to copy the database data before using it to generate the HTML table. It's just my humble opinion, but using dynamic SQL to generate dynamic SQL is also counter-intuitive.
Furthermore, database data values that contain HTML tags (or, worse still, malformed HTML tags) need to be escaped, so that they can be rendered correctly. For example, database data such as "value > 10" needs to generate the HTML "<td>value > 10</td>".
The following code addresses all of the above points, by using the built-in FOR XML clause:
CREATE PROCEDURE dbo.spCustomTable2HTML
#TABLENAME nvarchar(500),
#TBL_STYLE nvarchar(1024) = '',
#HDR_STYLE nvarchar(1024) = '',
#OUTPUT nvarchar(MAX) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
-- Declare variables
DECLARE #Columns nvarchar(MAX) = '',
#Data nvarchar(MAX),
#SQL nvarchar(MAX);
-- Snapshot columns (to force use of tempdb)
IF OBJECT_ID('tempdb.dbo.##spCustomTable2HTMLColumns') IS NOT NULL
BEGIN
DROP TABLE ##spCustomTable2HTMLColumns;
END
SET #SQL =
'SELECT TOP 0 *
INTO ##spCustomTable2HTMLColumns
FROM ' + #TABLENAME;
EXEC sp_executesql #SQL;
-- Build header row
SET #OUTPUT = (SELECT name AS td
FROM tempdb.sys.columns
WHERE object_id = OBJECT_ID('tempdb.dbo.##spCustomTable2HTMLColumns')
ORDER BY column_id
FOR XML RAW(''), ELEMENTS);
SET #OUTPUT += '</tr>'
-- Build column list
SELECT #Columns += '[' + name + '] AS td,'
FROM tempdb.sys.columns
WHERE object_id = OBJECT_ID('tempdb.dbo.##spCustomTable2HTMLColumns')
ORDER BY column_id;
SET #Columns = LEFT(#Columns, LEN(#Columns) - 1); -- Strip trailing comma
-- Delete columns snapshot
DROP TABLE ##spCustomTable2HTMLColumns;
-- Build data rows
SET #SQL =
'SET #Data = CONVERT(varchar(MAX),
(SELECT ' + #Columns +
' FROM ' + #TABLENAME +
' FOR XML RAW (''tr''), ELEMENTS XSINIL))';
EXEC sp_executesql #SQL, N'#Data NVARCHAR(MAX) OUTPUT', #Data = #Data OUTPUT;
SET #Data = REPLACE(#Data, ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"', ''); -- Remove XSI namespace
SET #Data = REPLACE(#Data, ' xsi:nil="true"', ''); -- Remove XSI attributes
SET #OUTPUT += #Data;
-- Prefix table/row headers
SET #OUTPUT = REPLACE(#OUTPUT, ' ', ' '); -- Use non-breaking spaces
SET #OUTPUT = REPLACE(#OUTPUT, '</tr>', '</tr>' + CHAR(13) + CHAR(10)); -- Add new line per row (to improve rendering in Microsoft Outlook)
SET #OUTPUT = '<table ' + #TBL_STYLE + '>' +
'<tr ' + #HDR_STYLE + '>' +
#OUTPUT +
'</table>';
END
Using SQL Server 2008, I would like to duplicate one row of a table, without knowing the field names. My key issue: as the table grows and mutates over time, I would like this copy-script to keep working, without me having to write out 30+ ever-changing fields, ugh.
Also at issue, of course, is IDENTITY fields cannot be copied.
My code below does work, but I wonder if there's a more appropriate method than my thrown-together text string SQL statement?
So thank you in advance. Here's my (yes, working) code - I welcome suggestions on improving it.
Todd
alter procedure spEventCopy
#EventID int
as
begin
-- VARS...
declare #SQL varchar(8000)
-- LIST ALL FIELDS (*EXCLUDE* IDENTITY FIELDS).
-- USE [BRACKETS] FOR ANY SILLY FIELD-NAMES WITH SPACES, OR RESERVED WORDS...
select #SQL = coalesce(#SQL + ', ', '') + '[' + column_name + ']'
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'EventsTable'
and COLUMNPROPERTY(OBJECT_ID('EventsTable'), COLUMN_NAME, 'IsIdentity') = 0
-- FINISH SQL COPY STATEMENT...
set #SQL = 'insert into EventsTable '
+ ' select ' + #SQL
+ ' from EventsTable '
+ ' where EventID = ' + ltrim(str(#EventID))
-- COPY ROW...
exec(#SQL)
-- REMEMBER NEW ID...
set #EventID = ##IDENTITY
-- (do other stuff here)
-- DONE...
-- JUST FOR KICKS, RETURN THE SQL STATEMENT SO I CAN REVIEW IT IF I WISH...
select EventID = #EventID, SQL = #SQL
end
No, there isn't any magic way to say "SELECT all columns except <foo>" - the way you're doing it is how you'll have to do it (the hack in the other answer aside).
Here is how I would alter your code, with these changes (some are hyperlinked so you can read my opinion about why):
use sys.columns over INFORMATION_SCHEMA.COLUMNS
use nvarchar instead of varchar
use scope_identity instead of ##identity
use sp_executesql instead of exec
use stuff instead of coalesce
use SET NOCOUNT ON
add semi-colons
use the schema prefix
use QUOTENAME since it's safer than '[' + ... + ']'
ALTER PROCEDURE dbo.spEventCopy
#EventID INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += ',' + QUOTENAME(name)
FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.EventsTable')
AND is_identity = 0;
SET #sql = STUFF(#sql, 1, 1, '');
SET #sql = N'INSERT dbo.EventsTable(' + #sql + ')
SELECT ' + #sql + ' FROM dbo.EventsTable
WHERE EventID = ' + CONVERT(VARCHAR(12), #EventID) + ';';
EXEC sp_executesql #sql;
SELECT #EventID = SCOPE_IDENTITY();
-- do stuff with the new row here
SELECT EventID = #EventID, SQL = #SQL;
END
If you know the what your identity column is called (and it won't be the column changing), you could do this:
SELECT * INTO #dummy FROM EventsTable where EventID = #EventID;
ALTER TABLE #dummy
DROP COLUMN MyIdentityColumn
INSERT EventsTable SELECT * FROM #dummy
DROP TABLE #dummy
Since a table can only every have one identity column, specifying that in the query shouldn't limit you too much.
As Aaron Bertrand points out, there are risks associated with this approach. Please read the discussion in the comments below.
I'm trying my first dynamic sql stored procedure. I need to append the exact same records into multiple tables with the same column names. What I have compiles, but when it runs I get 'invalid column name 'TradeDate. The driver sproc is first below, then the sproc containing the dynamic statement. If anyone could help, that'd be great..
ALTER PROCEDURE dbo.StoredProcedure2
AS
DECLARE #tableName varchar(120)
SET #tableName = 'tblDailyATR'
EXEC sprocAddDatesAndSymbolsToAggregatedStudy #tableName
RETURN
ALTER PROCEDURE dbo.sprocAddDatesAndSymbolsToAggregatedStudy
#table varchar(120)
AS
DECLARE #tableName varchar(120)
SET #tableName = #table
EXEC(
'INSERT INTO ' + #tableName + '(Symbol, TradeDate)
SELECT Symbol, TradingDate
FROM (SELECT tblSymbolsMain.Symbol, tblTradingDays.TradingDate
FROM tblSymbolsMain CROSS JOIN tblTradingDays
WHERE (tblTradingDays.TradingDate <= dbo.NextAvailableDataDownloadDate())) AS T1
WHERE (NOT EXISTS (SELECT TradeDate, Symbol
FROM' + #tableName +
' WHERE (TradeDate = T1.TradingDate) AND (Symbol = T1.Symbol)))')
RETURN
You're missing a space after the "FROM" in this line:
FROM' + #tableName +
Should be
FROM ' + #tableName +
Otherwise it's going to try running SELECT FROMTABLE.