I have a query like
select *
from tableName
where x='3'
This gives me back some number of results, but I don't want to see any columns where every row is Null. Is there an easy way to filter those out?
No there isn't.
What you are after is some syntax like
select <column list::filter(where all rows are NULL)>
However, it just doesn't make sense. Not in a general sense. It might look cool when used in SSMS or a one-off query tool, but for day-to-day usage by Joe Public in programs like ASP.Net, who'd want an unpredictable number of columns?
Now if you really wanted to do it, this can be achieved with dynamic SQL, but it would have to be coded ONCE-PER-TABLE that you want to query this way.
You can try some crazy stuff with dynamics SQL but I really wouldn’t recommend this.
This query checks if first two columns have all null values and then adds them to select statement. If you really want to go this way you can to the same for all columns in your table.
DECLARE #sql nvarchar(1000)
DECLARE #columnList nvarchar(1000)
SET #columnList = ''
DECLARE #tableRowCount int
DECLARE #columnRowCount int
SET #tableRowCount = (select COUNT(*) from tableName)
SET #columnRowCount = (select COUNT(*) from tableName where column1 is null)
IF #tableRowCount <> #columnRowCount
SET #columnList = #columnList + 'column1, '
SET #columnRowCount = (select COUNT(*) from tableName where column2 is null)
IF #tableRowCount <> #columnRowCount
SET #columnList = #columnList + 'column2, '
IF LEN(#columnList) > 0
SET #sql = 'SELECT ' + SUBSTRING(#columnList,1, LEN(#columnList) - 1) + ' FROM tableName'
ELSE
SET #sql = 'SELECT * FROM tableName'
EXEC(#sql)
Related
Is there a way to create a view of a table without the columns that are null.
What i mean by that is that i want a view where there are only columns where at least one row has some data in that column and is not empty.
In this example, the result would be a view with columns A and C
I'm using Microsoft SQL and the data in db aren't going to be updated.
That is not possible via the view, because the view cannot handle the dynamic column set. It is possible only via the stored procedure returning the result set. I made not a very beautiful and not generic decision, but I think you can upgrade it if you can.
First, we need to create a function which returns the set of columns which have at least one non-null value:
create function getTheColumnList() returns nvarchar(max) as
begin
declare #columnlist nvarchar(max);
set #columnlist = ''
declare #totalrows int;
set #totalrows = (select
count(*)
from table_name)
if (select
count(*)
from table_name
where A is null) < #totalrows
set #columnlist = #columnlist + 'A, '
if (select
count(*)
from table_name
where B is null) < #totalrows
set #columnlist = #columnlist + 'B, '
if (select
count(*)
from table_name
where C is null) < #totalrows
set #columnlist = #columnlist + 'C, '
set #columnlist = replace(#columnlist + '%', ', %', '')
return #columnlist;
end
go
Then we need to use this function in a dynamic SQL to construct the needed query. It can be done inside the stored procedure:
create proc getNottNullFromTableName as
declare #SQL nvarchar(1000)
declare #columnist varchar(50)
set #columnist = dbo.getTheColumnList();
set #SQL = 'SELECT ' + #columnist + ' FROM table_name';
exec (#SQL)
go
Finally, invoke that procedure:
I need some help with using a cursor and variable to populate a query. I am using SQL Server 2008 R2.
What I am trying to do is populate a temp table with inserts, run through the one column of data to generate a variable that will then populate a query that will check the number of rows in a table. Here is what I have so far:
IF OBJECT_ID('tempdb..#part_tables') IS NOT NULL DROP TABLE #part_tables
create table #Part_tables
(table_Name nvarchar(100))
Insert INTO #Part_Tables (table_name)
SELECT [InstancesTable] FROM [BAMPrimaryImport].[dbo].[bam_Metadata_Partitions]
WHERE [ArchivingInProgress]=0 and ArchivedTime IS NULL
and creationtime < dateadd(DD,-21,getdate())
GO
Insert INTO #Part_Tables (table_name)
SELECT [RelationshipsTable] FROM [BAMPrimaryImport].[dbo].[bam_Metadata_Partitions]
WHERE [ArchivingInProgress]=0 and ArchivedTime IS NULL
and creationtime < dateadd(DD,-21,getdate())
GO
DECLARE #count_query VARCHAR(MAX)
DECLARE #Value NVARCHAR(100)
SET #Value ='Select Table_Name from #Part_Tables'
SET #count_query ='
select count (*) from #Value with (NOLOCK)'
WHILE 1 = 1
BEGIN
EXEC(#count_query + ' option(maxdop 5) ')
IF ##rowcount < 1 BREAK;
END
If this will work, great! If you have a different / better way to do it, I would appreciate any guidance that someone could offer.
Here is a much simpler way to get the row counts from those tables. No need for cursors or while loops. And be careful with that NOLOCK hint...it can do a lot more than just dirty reads. http://blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere/
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select count(*) from ' + QUOTENAME(InstancesTable) + ' UNION ALL '
FROM [BAMPrimaryImport].[dbo].[bam_Metadata_Partitions]
WHERE [ArchivingInProgress] = 0
and ArchivedTime IS NULL
and creationtime < dateadd(Day, -21, getdate())
set #SQL = LEFT(#SQL, len(#SQL) - 10)
select #SQL --uncomment exec statement below when satisfied this is correct
--exec sp_executesql #SQL
My table has all the column names
(There are more than 80 columns, I can't change the column names now)
in the format of '_'. Like First_Name, Last_Name,...
So i want to use select * from table instead
of using AS.
I want to select them by removing '_' in one statement. Anyway i can do it?
something like Replace(coulmnName, '_','') in select statement ?
Thanks
You can simply rename the column in your query. For example:
SELECT FIRST_NAME [First Name],
LAST_NAME [Last Name]
FROM UserTable
You can also use the AS keyword but this is optional. Also note that if you don't want to do this on every query you can use this process to create a view with renamed columns. Then you can use SELECT * the way you want to (although this is considered a bad idea for many reasons).
Best of luck!
Alternative - Map In The Client Code:
One other alternative is to do the mapping in the client code. This solution is going to depend greatly on your ORM. Most ORM's (such as LINQ or EF) will allow you to remap. If nothing else you could use AutoMapper or similar to rename the columns on the client using convention based naming.
You can't do this in a single statement unless you're using dynamic SQL. If you're just trying to generate code, you can run a query against Information_Schema and get the info you want ...
DECLARE #MaxColumns INT
DECLARE #TableName VARCHAR(20)
SET #TableName = 'Course'
SELECT #MaxColumns = MAX(ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #TableName
SELECT Col
FROM
(
SELECT 0 Num, 'SELECT' Col
UNION
SELECT ROW_NUMBER() OVER (PARTITION BY TABLE_NAME ORDER BY ORDINAL_POSITION) Num, ' [' + COLUMN_NAME + '] AS [' + REPLACE(COLUMN_NAME, '_', '') + ']' + CASE WHEN ORDINAL_POSITION = #MaxColumns THEN '' ELSE ',' END
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
UNION
SELECT #MaxColumns + 1 Num, 'FROM ' + #TableName
) s
ORDER BY num
The question intrigued me and I did find one way. It makes it happen but if you just wanted to give a lot of aliases one time in one query I wouldn't recommend it though.
First I made a stored procedure that extracts all the column names and gives them an alias without '_'.
USE [DataBase]
GO
IF OBJECT_ID('usp_AlterColumnDisplayName', 'P') IS NOT NULL
DROP PROCEDURE usp_AlterColumnDisplayName
GO
CREATE PROCEDURE usp_AlterColumnDisplayName
#TableName VARCHAR(50)
,
#ret nvarchar(MAX) OUTPUT
AS
Select #ret = #ret + [Column name]
From
(
SELECT ([name] + ' AS ' + '[' + REPLACE([name], '_', ' ') + '], ') [Column name]
FROM syscolumns
WHERE id =
(Select id
From sysobjects
Where type = 'U'
And [name] = #TableName
)
) T
GO
Then extract that string and throw it into another string with a query-structure.
Execute that and you are done.
DECLARE #out NVARCHAR(MAX), #DesiredTable VARCHAR(50), #Query NVARCHAR(MAX)
SET #out = ''
SET #DesiredTable = 'YourTable'
EXEC usp_AlterColumnDisplayName
#TableName = #DesiredTable,
#ret = #out OUTPUT
SET #out = LEFT(#out, LEN(#out)-1) --Removing trailing ', '
SET #Query = 'Select ' + #out + ' From ' + #DesiredTable + ' WHERE whatever'
EXEC sp_executesql #Query
If you just wanted to give a lot of aliases at once without sitting and typing it out for 80+ columns I would rather suggest doing that with one simple SELECT statement, like the one in the sp, or in Excel and then copy paste into your code.
I have fairly new to using SQL, currently I have a table that has a column that contains the names of all the tables I want to use for one query, so what I want to do is to loop through that column and go to every single one of these tables and then search one of their columns for a value (there could be multiple values), so whenever a table contains the value, I will list the name of the table. Could someone give me a hint of how this is done? Is cursor needed for this?
I don't have enough reputation to comment but is the table with the column that contain the table names all in one column, meaning that all the table names are comma separated or marked with some sort of separator? This would cause the query to be a little more complicated as you would have to take care of that before you start looping through your table.
However, this would require a cursor, as well as some dynamic sql.
I will give a basic example of how you can go about this.
declare #value varchar(50)
declare #tableName varchar(50)
declare #sqlstring nvarchar(100)
set #value = 'whateveryouwant'
declare #getTableName = cursor for
select tableName from TablewithTableNames
OPEN #getTableName
fetch NEXT
from #getTableName into #tableName
while ##FETCH_STATUS = 0
BEGIN
set #sqlstring = 'Select Count(*) from ' + #tableName + 'where ColumnNameYouwant = ' + #value
exec #sqlstring
If ##ROWcount > 0
insert into #temptable values (#tableName)
fetch next
from #getTableName into #tableName
END
select * from #temptable
drop table #temptable
close #getTableName
deallocate #getTableName
I'm currently not able to test this out as for time constraint reasons, but this is how I would go about doing this.
You could try something like this:
--Generate dynamic SQL
DECLARE #TablesToSearch TABLE (
TableName VARCHAR(50));
INSERT INTO #TablesToSearch VALUES ('invoiceTbl');
DECLARE #SQL TABLE (
RowNum INT,
SQLText VARCHAR(500));
INSERT INTO
#SQL
SELECT
ROW_NUMBER() OVER (ORDER BY ts.TableName) AS RowNum,
'SELECT * FROM ' + ts.TableName + ' WHERE ' + c.name + ' = 1;'
FROM
#TablesToSearch ts
INNER JOIN sys.tables t ON t.name = ts.TableName
INNER JOIN sys.columns c ON c.object_id = t.object_id;
--Now run the queries
DECLARE #Count INT;
SELECT #Count = COUNT(*) FROM #SQL;
WHILE #Count > 0
BEGIN
DECLARE #RowNum INT;
DECLARE #SQLText VARCHAR(500);
SELECT TOP 1 #RowNum = RowNum, #SQLText = SQLText FROM #SQL;
EXEC (#SQLText);
DELETE FROM #SQL WHERE RowNum = #RowNum;
SELECT #Count = COUNT(*) FROM #SQL;
END;
You would need to change the "1" I am using as an example to the value you are looking for and probably add a CONVERT/ CAST to make sure the column is the right data type?
You actually said that you wanted the name of the table, so you would need to change the SQL to:
'SELECT ''' + ts.TableName + ''' FROM ' + ts.TableName + ' WHERE ' + c.name + ' = 1;'
Another thought, it would probably be best to insert the results from this into a temporary table so you can dump out the results in one go at the end?
I know that I can search for a term in one column in a table in t-sql by using like %termToFind%. And I know I can get all columns in a table with this:
SELECT *
FROM MyDataBaseName.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'MyTableName`
How can I perform a like comprparison on each of the columns of a table? I have a very large table so I can't just spell out LIKE for each column.
As always, I'll suggest xml for this (I'd suggest JSON if SQL Server had native support for it :) ). You can try to use this query, though it could perform not so well on large number of rows:
;with cte as (
select
*,
(select t.* for xml raw('data'), type) as data
from test as t
)
select *
from cte
where data.exist('data/#*[local-name() != "id" and contains(., sql:variable("#search"))]') = 1
see sql fiddle demo for more detailed example.
Important note by Alexander Fedorenko in comments: it should be understood that contains function is case-sensitive and uses xQuery default Unicode code point collation for the string comparison.
More general way would be to use dynamic SQL solution:
declare #search nvarchar(max)
declare #stmt nvarchar(max)
select #stmt = isnull(#stmt + ' or ', '') + quotename(name) + ' like #search'
from sys.columns as c
where c.[object_id] = object_id('dbo.test')
--
-- also possible
--
-- select #stmt = isnull(#stmt + ' or ', '') + quotename(column_name) + ' like #search'
-- from INFORMATION_SCHEMA.COLUMNS
-- where TABLE_NAME = 'test'
select #stmt = 'select * from test where ' + #stmt
exec sp_executesql
#stmt = #stmt,
#params = N'#search nvarchar(max)',
#search = #search
sql fiddle demo
I'd use dynamic SQL here.
Full credit - this answer was initially posted by another user, and deleted. I think it's a good answer so I'm re-adding it.
DECLARE #sql NVARCHAR(MAX);
DECLARE #table NVARCHAR(50);
DECLARE #term NVARCHAR(50);
SET #term = '%term to find%';
SET #table = 'TableName';
SET #sql = 'SELECT * FROM ' + #table + ' WHERE '
SELECT #sql = #sql + COALESCE('CAST('+ column_name
+ ' as NVARCHAR(MAX)) like N''' + #term + ''' OR ', '')
FROM INFORMATION_SCHEMA.COLUMNS WHERE [TABLE_NAME] = #table
SET #sql = #sql + ' 1 = 0'
SELECT #sql
EXEC sp_executesql #sql
The XML answer is cleaner (I prefer dynamic SQL only when necessary) but the benefit of this is that it will utilize any index you have on your table, and there is no overhead in constructing the XML CTE for querying.
In case someone is looking for PostgreSQL solution:
SELECT * FROM table_name WHERE position('your_value' IN (table_name.*)::text)>0
will select all records that have 'your_value' in any column. Didn't try this with any other database.
Unfortunately this works as combining all columns to a text string and then searches for a value in that string, so I don't know a way to make it match "whole cell" only. It will always match if any part of any cell matches 'your_value'.