Compare Identical Columns of 2 Different Table for Mismatch Data - sql-server

I have the following query to check if usernames are the exact same between to tables
SELECT
--Username
a.username as 'TABLE_1_USERNAME',
b.username as 'TABLE_2_USERNAME',
CASE
WHEN a.username IS NULL AND b.username IS NULL THEN 'True'
WHEN a.username = b.username THEN 'True'
ELSE 'False'
END AS 'Is Equal?'
FROM User a
JOIN User_Group b ON a.id = b.id
This works great to tell me if usernames differ for any reason, but what I'd like to do is recursively compare each column between the User table and the User_Group Table (without having to write each one out) - both of the table's column names are identical. Is this possible in SQL Server?
Leaving out casing checks and trimming for brevity

Try this. Except shows any rows in the two queries that don't fully match
SELECT * FROM User_Group
EXCEPT
SELECT * FROM User

Here's a dynamic sql method for doing literally what you're asking for:
DECLARE #cols TABLE (id int identity(1,1), colname varchar(25))
INSERT INTO #cols (colname) select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'User' AND COLUMN_NAME <> 'id'
declare #sql nvarchar(max)
select #sql = 'SELECT a.id, '
declare #maxval int
select #maxval = max(id) from #cols
declare #c int = 1
declare #curcol varchar(25)
while #c <= #maxval
begin
if #c > 1
select #sql += ', '
select #curcol = colname from #cols where id = #c
select #sql += 'a.' + #curcol + ' as ''Table_1_' + #curcol + ''', b.' + #curcol + ' as ''Table_2_' + #curcol + ''',
CASE
WHEN a.' + #curcol + ' IS NULL AND b.' + #curcol + ' IS NULL THEN ''True''
WHEN a.' + #curcol + ' = b.' + #curcol + ' THEN ''True''
ELSE ''False''
END AS ''Is Equal?'''
set #c = #c + 1
end
select #sql += 'FROM [User] a
INNER JOIN User_Group b ON a.id = b.id'
exec sp_executesql #sql
With the following test data:
create table [User] (id int, username varchar(10), fname varchar(25), lname varchar(25))
create table [User_Group] (id int, username varchar(10), fname varchar(25), lname varchar(25))
insert into [User] values (1, 'user1', 'fname1', 'lname1')
insert into [User_Group] values (1, 'user1', 'fname1', 'lname1')
insert into [User] values (2, 'user2', 'fname2', 'lname2')
insert into [User_Group] values (2, 'user2', 'fnameasdf', 'lnamesdfg')
It generates this output:
id Table_1_username Table_2_username Is Equal? Table_1_fname Table_2_fname Is Equal? Table_1_lname Table_2_lname Is Equal?
1 user1 user1 True fname1 fname1 True lname1 lname1 True
2 user2 user2 True fname2 fnameasdf False lname2 lnamesdfg False
Here's the query it generates:
SELECT a.id, a.username as 'Table_1_username', b.username as 'Table_2_username',
CASE
WHEN a.username IS NULL AND b.username IS NULL THEN 'True'
WHEN a.username = b.username THEN 'True'
ELSE 'False'
END AS 'Is Equal?', a.fname as 'Table_1_fname', b.fname as 'Table_2_fname',
CASE
WHEN a.fname IS NULL AND b.fname IS NULL THEN 'True'
WHEN a.fname = b.fname THEN 'True'
ELSE 'False'
END AS 'Is Equal?', a.lname as 'Table_1_lname', b.lname as 'Table_2_lname',
CASE
WHEN a.lname IS NULL AND b.lname IS NULL THEN 'True'
WHEN a.lname = b.lname THEN 'True'
ELSE 'False'
END AS 'Is Equal?'FROM [User] a
INNER JOIN User_Group b ON a.id = b.id

Related

Inserting records from a table to another using dynamic SQL with conditions to convert data type

I have two tables whose structures as follows:
table_A
CREATE TABLE table_A
(
col_a varchar(100),
col_b bigint,
col_c datetime
)
table_b
--Note that columns are same--
CREATE TABLE table_B
(
col_a varchar(10),
col_b varchar(10),
col_c varchar(20)
)
Now I want to INSERT data into table_A from table_B with proper data type conversion.
Below is the SQL string:
INSERT INTO table_A(col_a,col_b,col_c)
SELECT CONVERT(varchar,col_a),CONVERT(INT,col_b),CONVERT(datetime,col_c) FROM table_B
So far so good.
Now I want generate the SQL dynamically with the help of INFORMATION_SCHEMA.COLUMNS.
For this I have followed the below steps:
Step 1:
Join the Information Schema for the above two tables viz table_A and table_B and store them in a #TempTable. Lets assume that #TempTable has an ID column that is IDENTITY(1,1) but that doesn't follow any sequence like 1,2,3...(Typically this happens in Synapse SQL)
INSERT INTO #TempTable
SELECT S.COLUMN_NAME AS Src_Col,
S.DATA_TYPE AS Src_dtype,
D.COLUMN_NAME AS Dest_Col,
D.DATA_TYPE AS Dest_dtype,
CASE WHEN S.DATA_TYPE NOT LIKE D.DATA_TYPE THEN
'CONVERT('+ '''' + D.DATA_TYPE + '''' + ',' + '''' + S.DATA_TYPE + '''' + ')'
ELSE S.DATA_TYPE AS Modified_Col
FROM INFORMATION_SCHEMA S
JOIN INFORMATION_SCHEMA.COLUMNS D
ON S.COLUMN_NAME = D.COLUMN_NAME AND S.TABLE_NAME = REPLACE(D.TABLE_NAME,'_B','_A')
Step 2:
Iterate over #TempTable to fetch the Modified_Col values
SET #Max_ID = (SELECT MAX(ID) FROM #TempTable);
SET #Min_ID = (SELECT MIN(ID) FROM #TempTable);
SET #ColToInsert = '';
SET #Dest_Col = '';
WHILE #Min_ID <= #Max_ID
BEGIN
SET #ColToInsert = (SELECT #ColToInsert + Modified_Col FROM #TempTable T WHERE T.ID = #Min_ID);
SET #Dest_Col = (SELECT #Dest_Col + Dest_Col FROM #TempTable T WHERE T.ID = #Min_ID);
SET #Min_ID = #Min_ID + 1;
END
Step 3:
Use that #ColToInsert in the below Dynamic SQL
SET #DySQL = 'INSERT INTO Table_A(' + #Dest_Col + ') SELECT ' + #ColToInsert + ' FROM table_B';
exec (#DySQL);
Now at this step 3 I am not getting the expected result. No data is getting inserted into table_A. I can understand that in the CASE statement I have to make some fixes so that convert... portion becomes a string. And I am not able to do so.
Any clue would be appreciated.
I don't understand why you need the temp table at all. You just need to aggregate using STRING_AGG.
You also need to quote the objects and columns using QUOTENAME, and you should use sys.columns etc rather than INFORMATION_SCHEMA, which is for compatibility only.
DECLARE #tableA sysname = 't';
DECLARE #tableB sysname = 's';
DECLARE #sql nvarchar(max) = (
SELECT CONCAT(
'INSERT INTO ',
QUOTENAME(#tableA),
'(',
STRING_AGG(CAST(QUOTENAME(cA.name) AS nvarchar(max)), ', '),
')
SELECT ',
STRING_AGG(
CASE WHEN cA.user_type_id <> cB.user_type_id THEN
CONCAT(
'CONVERT(',
typ.name,
CASE
WHEN typ.name IN ('varchar','nvarchar','char','nchar','varbinary','binary')
THEN CONCAT('(', CASE WHEN cA.max_length = -1 THEN 'max' END, NULLIF(cA.max_length, -1), ')')
WHEN typ.name IN ('datetime2','datetimeoffset','time','float','real')
THEN CONCAT('(', cA.scale, ')')
WHEN typ.name IN ('float','real')
THEN CONCAT('(', cA.precision, ')')
WHEN typ.name IN ('decimal','numeric')
THEN CONCAT('(', cA.precision, ',', cA.scale, ')')
END,
', ',
CAST(QUOTENAME(cB.name) AS nvarchar(max)),
')'
)
ELSE
CAST(QUOTENAME(cB.name) AS nvarchar(max))
END
, ', '),
'
FROM ',
QUOTENAME(#tableB)
)
FROM sys.columns cA
JOIN sys.tables tA ON ta.object_id = cA.object_id AND tA.name = #tableA
JOIN sys.types typ ON typ.user_type_id = cA.user_type_id
JOIN sys.columns cB ON cB.name = cA.name
JOIN sys.tables tB ON tB.object_id = cB.object_id AND tB.name = #tableB
);
PRINT #sql; -- your friend
EXEC sp_executesql #sql;
db<>fiddle

What is the right way to count number of records returned by group by query

How do I count the number of actual records returned by a group by query
For e.g
-- Exec SP_GET_ITEM_STOCK 0,10,NULL,'Charger'
ALTER PROCEDURE [dbo].[SP_GET_ITEM_STOCK]
#startRowIndex int ,
#pageSize int,
#ItemID bigint = null,
#ItemName varchar(250) = null
AS
BEGIN
DECLARE #SQL varchar(MAX)
DECLARE #SQLWHERE varchar(MAX)
SET #SQL = 'WITH DATA AS (
select
ROW_NUMBER() OVER (ORDER BY Item_ID) ''SNo'',
Item_ID,
Item_Name,
SUM(Inward) as Total_Purchase,
SUM(Outward) as Total_Sale,
(sum(Inward) - sum(outward))as Balance_Stock'
Set #SQLWHERE = ' from Item_Ledger_Details where Active = 1'
IF #ItemID IS NOT NULL and #ItemID <> ''
SET #SQLWHERE = #SQLWHERE + ' and Item_ID = ' + CONVERT(varchar,#ItemID) + ''
IF #ItemName IS NOT NULL and #ItemName <> ''
SET #SQLWHERE = #SQLWHERE + ' and Item_Name like ''%' + #ItemName + '%'''
SET #SQL = #SQL + #SQLWHERE + ' group by Item_ID,Item_Name) SELECT * FROM DATA WHERE SNo BETWEEN ' + CONVERT(Varchar,#startRowIndex) + ' AND ' + CONVERT(Varchar,#startRowIndex+#pageSize) + ' ORDER BY SNo'
EXEC(#SQL +';SELECT COUNT(*) ''Count'' '+ #SQLWHERE)
print(#SQL)
END
Which returns:
I need to count the above first result records to get 1 + 1 = 2 in second result where I get count = 48
Continue Anand Answer. I've just modify his query. Below query is solved my answer. But I think may be this query needs optimization.
-- SP_GET_ITEM_STOCK 0,10,NULL,NULL
ALTER PROCEDURE [dbo].[SP_GET_ITEM_STOCK]
#startRowIndex int,
#pageSize int,
#ItemID bigint = null,
#ItemName varchar(250) = null
AS
BEGIN
Declare #Temp Table (
SNo bigint,
Item_ID bigint,
Item_Name varchar(max),
Total_Purchase money,
Total_Sale money,
Balance_Stock money
);
WITH DATA AS (
select
ROW_NUMBER() OVER (ORDER BY Item_ID) as SNo,
Item_ID,
Item_Name,
SUM(Inward) as Total_Purchase,
SUM(Outward) as Total_Sale,
(sum(Inward) - sum(outward))as Balance_Stock
from Item_Ledger_Details
where Active = 1
and (coalesce(#ItemID, '') = '' or Item_ID = CONVERT(varchar,#ItemID))
and ( coalesce(#ItemName, '') = '' or Item_Name like '%' + #ItemName + '%')
group by Item_ID,
Item_Name
)
insert into #Temp
SELECT *
FROM DATA
WHERE SNo BETWEEN #startRowIndex AND #startRowIndex+#pageSize
ORDER BY SNo
select * from #temp
SELECT COUNT(*) as Count from #temp
END
There, I've cleaned it up:
ALTER PROCEDURE [dbo].[SP_GET_ITEM_STOCK]
#startRowIndex int,
#pageSize int,
#ItemID bigint = null,
#ItemName varchar(250) = null
AS
BEGIN
;WITH DATA AS (
select
ROW_NUMBER() OVER (ORDER BY Item_ID) as SNo,
Item_ID,
Item_Name,
SUM(Inward) as Total_Purchase,
SUM(Outward) as Total_Sale,
(sum(Inward) - sum(outward))as Balance_Stock
from Item_Ledger_Details
where Active = 1
and (coalesce(#ItemID, '') = '' or Item_ID = CONVERT(varchar,#ItemID))
and ( coalesce(#ItemName, '') = '' or Item_Name like '%' + #ItemName + '%')
group by Item_ID,
Item_Name
)
SELECT *
FROM DATA
WHERE SNo BETWEEN #startRowIndex AND #startRowIndex+#pageSize
ORDER BY SNo
SELECT COUNT(*) as Count
from DATA
END

How to replace while loop?

I've tried to simplify following sql query, it takes more time due to the while loop.
DECLARE #TEMP TABLE
(
ID NUMERIC(18,0) IDENTITY (1, 1) PRIMARY KEY NOT NULL,
NAME VARCHAR(1000),
CATEGORY_ID INT,
TYPE1 VARCHAR(100),
VALUE VARCHAR(6000),
TYPE2 VARCHAR(100)
)
INSERT INTO #TEMP
SELECT
NAME ,
A.CATEGORY_ID,
A.TYPE1,
A.VALUE,
A.TYPE2
FROM
DBO.TABLE1 A,
DBO.TABLE2 B
WHERE
A.CATEGORY_ID=B.CATEGORY_ID
ORDER BY A.CATEGORY_ID
DECLARE #ROWCNT INT=1 , #ROWS INT=0 , #NAME VARCHAR(100),#TYPE1 VARCHAR(100)
,#STAT CHAR(1)='Y'
, #VALUE VARCHAR(6000)
,#COND VARCHAR(8000)
, #TYPE2 VARCHAR(100)
SELECT #COND='SELECT * FROM TABLE3 '
SELECT #ROWCNT = #ROWCNT , #ROWS = (#ROWCNT-1)+ COUNT(1) FROM #TEMP
WHILE #ROWCNT <= #ROWS
BEGIN
SELECT #NAME = NAME ,
#TYPE1 = LTRIM(RTRIM(TYPE1)) ,
#VALUE = VALUE,
#TYPE2 = ISNULL(TYPE2,'')
FROM #TEMP WHERE ID = #ROWCNT
IF #STAT='Y'
BEGIN
IF #TYPE1 = 'SQL'
BEGIN
SELECT #COND=#COND + ' A'+'.'+(#NAME)+' '+#TYPE1+ ' ('''+#VALUE+''') '+#TYPE2+' '+CHAR(13)
END
ELSE
BEGIN
SELECT #COND=#COND + ' A'+'.'+(#NAME)+' '+#TYPE1+ ''''+#VALUE+''' '+#TYPE2+' '++CHAR(13)
END
END
SELECT #ROWCNT=#ROWCNT+1;
END
PRINT (#COND)
the important thing to replace while loop instead of CTE or something else.
can any one sort up this problem.Thanks in advance
It looks like you need something like the below.
DECLARE #COND VARCHAR(8000) ='SELECT * FROM TABLE3 '
+ (SELECT ' A' + '.' + NAME + ' ' + LTRIM(RTRIM(TYPE1))
+ CASE
WHEN LTRIM(RTRIM(TYPE1)) = 'SQL' THEN ' (''' + VALUE + ''') '
ELSE VALUE
END
+ ISNULL(TYPE2, '') + ' ' + CHAR(13)
FROM #TEMP
ORDER BY ID
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(8000)');
PRINT #COND
The string generated isn't valid SQL though

SQL Server: Dynamic columns, insert into SQL table

I have a table named SQL_Standard:
ID name sql_name sourceTable
1 member_id mem_id tableA
2 member_dob mem_dob tableA
3 member_email mem_email tableA
4 member_phone tableA
5 member_id mbr_id tableB
6 member_dob mbr_dob tableB
7 member_email tableB
8 member_phone tableB
9 member_id sub_id tableC
10 member_id emp_id tableC
11 member_dob sub_dob tableC
12 member_email tableC
13 member_phone sub_phn tableC
I also have tableA, tableB, tableC. Sql_name blank means that the column does not exists in that table. But we have standard name as member_id,member_dob, , member_email, member_phone because these are standard columns for all source tables.
tableA
mem_id mem_dob mem_email
1011 1986-05-05 bsi#yahoo.com
1012 1987-09-09 bw#gmail.com
tableB
mbr_id mbr_dob
5555 1965-02-09
tableC
sub_id emp_id sub_dob sub_phn
15 56 19474-02-05 808-888-8888
We ultimately want to load all the records from all source tables to the combined table. The columns which do not exists in sourcetable will not get loaded.
I want to achieve two goals here:
Load all records from source table to combined table
Where there are two names for source table (ID 9 and 10), concatenate
two columns into one.
Combined table:
Member_id member_dob member_email member_phone sourcetable
1011 1986-05-05 bsi#yahoo.com tableA
1012 1987-09-09 bw#gmail.com tableA
5555 1965-02-09 tableB
1556 19474-02-05 808-888-8888 tableC
My efforts:
DECLARE #memID VARCHAR(20)
DECLARE #DOB VARCHAR(20)
DECLARE #email VARCHAR( 20)
DECLARE #phone VARCHAR(20)
SET #source_table = (SELECT top 1 sourcetable from SQL_Standard )
SELECT #memID = sql_name
from SQL_Standard
where (name = 'member_id' and sourcetable = #source_table)
SELECT #DOB = sql_name
from SQL_Standard
where (name = 'member_dob' and sourcetable = #source_table)
SELECT #email = sql_name
from SQL_Standard
where (name = 'member_email' and sourcetable = #source_table)
SELECT #phone = sql_name
from SQL_Standard
where (name = 'member_phone' and sourcetable = #source_table)
SELECT #sqlStr = ' ;
WITH Tableinfo1 AS (SELECT [' + #memID + ']
,[' + #DOB + ']
,[' + #email + ']
,[' + #phone + ']
FROM [' + #source_table + '] )
SELECT * FROM Tableinfo1'
EXEC (#sqlstr)
IF #memID is not null or #DOB is not null or #email is not null or #phone is not null -- make sure at least 1 is there
BEGIN
SET #sqlStr = ' ; WITH Tableinfo1 AS (SELECT '
if #memID is not null
SET #sqlStr= #sqlStr + ' [' + #memID + '],'
if #DOB is not null
SET #sqlStr= #sqlStr + ' [' + #DOB + '],'
if #email is not null
SET #sqlStr= #sqlStr + ' [' + #email + '],'
if #phone is not null
SET #sqlStr= #sqlStr + ' [' + #phone + '],'
SET #sqlStr =#sqlStr + ' FROM [' + #source_table + '] )'
--sheer laziness below to remove last comma
SET #sqlStr =REPLACE(#sqlStr, ', FROM',' FROM')
END
Add clauses as needed based on however many columns you need. This is not ideal, but there is definitely code that you are running that you didn't post.
Incidentally, how are you getting anything other than TableA here
SET #source_table = (SELECT top 1 sourcetable from SQL_Standard )

How can I inexpensively determine if a column contains only NULL records?

I have a large table with 500 columns and 100M rows. Based on a small sample, I believe only about 50 of the columns contain any values, and the other 450 contain only NULL values. I want to list the columns that contain no data.
On my current hardware, it would take about 24 hours to query every column (select count(1) from tab where col_n is not null)
Is there a less expensive way to determine that a column is completely empty/NULL?
What about this:
SELECT
SUM(CASE WHEN column_1 IS NOT NULL THEN 1 ELSE 0) column_1_count,
SUM(CASE WHEN column_2 IS NOT NULL THEN 1 ELSE 0) column_2_count,
...
FROM table_name
?
You can easily create this query if you use INFORMATION_SCHEMA.COLUMNS table.
EDIT:
Another idea:
SELECT MAX(column_1), MAX(column_2),..... FROM table_name
If result contains value, column is populated. It should require one table scan.
Try this one -
DDL:
IF OBJECT_ID ('dbo.test2') IS NOT NULL
DROP TABLE dbo.test2
CREATE TABLE dbo.test2
(
ID BIGINT IDENTITY(1,1) PRIMARY KEY
, Name VARCHAR(10) NOT NULL
, IsCitizen BIT NULL
, Age INT NULL
)
INSERT INTO dbo.test2 (Name, IsCitizen, Age)
VALUES
('1', 1, NULL),
('2', 0, NULL),
('3', NULL, NULL)
Query 1:
DECLARE
#TableName SYSNAME
, #ObjectID INT
, #SQL NVARCHAR(MAX)
SELECT
#TableName = 'dbo.test2'
, #ObjectID = OBJECT_ID(#TableName)
SELECT #SQL = 'SELECT' + CHAR(13) + STUFF((
SELECT CHAR(13) + ', [' + c.name + '] = ' +
CASE WHEN c.is_nullable = 0
THEN '0'
ELSE 'CASE WHEN ' + totalrows +
' = SUM(CASE WHEN [' + c.name + '] IS NULL THEN 1 ELSE 0 END) THEN 1 ELSE 0 END'
END
FROM sys.columns c WITH (NOWAIT)
CROSS JOIN (
SELECT totalrows = CAST(MIN(p.[rows]) AS VARCHAR(50))
FROM sys.partitions p
WHERE p.[object_id] = #ObjectID
AND p.index_id IN (0, 1)
) r
WHERE c.[object_id] = #ObjectID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ') + CHAR(13) + 'FROM ' + #TableName
PRINT #SQL
EXEC sys.sp_executesql #SQL
Output 1:
SELECT
[ID] = 0
, [Name] = 0
, [IsCitizen] = CASE WHEN 3 = SUM(CASE WHEN [IsCitizen] IS NULL THEN 1 ELSE 0 END) THEN 1 ELSE 0 END
, [Age] = CASE WHEN 3 = SUM(CASE WHEN [Age] IS NULL THEN 1 ELSE 0 END) THEN 1 ELSE 0 END
FROM dbo.test2
Query 2:
DECLARE
#TableName SYSNAME
, #SQL NVARCHAR(MAX)
SELECT #TableName = 'dbo.test2'
SELECT #SQL = 'SELECT' + CHAR(13) + STUFF((
SELECT CHAR(13) + ', [' + c.name + '] = ' +
CASE WHEN c.is_nullable = 0
THEN '0'
ELSE 'CASE WHEN '+
'MAX(CAST([' + c.name + '] AS CHAR(1))) IS NULL THEN 1 ELSE 0 END'
END
FROM sys.columns c WITH (NOWAIT)
WHERE c.[object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ') + CHAR(13) + 'FROM ' + #TableName
PRINT #SQL
EXEC sys.sp_executesql #SQL
Output 2:
SELECT
[ID] = 0
, [Name] = 0
, [IsCitizen] = CASE WHEN MAX(CAST([IsCitizen] AS CHAR(1))) IS NULL THEN 1 ELSE 0 END
, [Age] = CASE WHEN MAX(CAST([Age] AS CHAR(1))) IS NULL THEN 1 ELSE 0 END
FROM dbo.test2
Results:
ID Name IsCitizen Age
----------- ----------- ----------- -----------
0 0 0 1
Could you check if colums idexing will help you reach some performance improve
CREATE UNIQUE NONCLUSTERED INDEX IndexName ON dbo.TableName(ColumnName)
WHERE ColumnName IS NOT NULL;
GO
SQL server query to get the list of columns in a table along with Data types, NOT NULL, and PRIMARY KEY constraints
Run SQL in best answer of above questions and generate a new query like below.
Select ISNULL(column1,1), ISNULL(column2,1), ISNULL(column3,1) from table
You would not need to 'count' all of the 100M records. When you simply back out of the query with a TOP 1 as soon as you hit a column with a not-null value, would save a lot of time while providing the same information.
500 Columns?!
Ok, the right answer to your question is: normalize your table.
Here's what happening for the time being:
You don't have an index on that column so SQL Server has to do a full scan of your humongous table.
SQL Server will certainly fully read every row (it means every columns even if you're only interested in one).
And since your row are most likely over 8kb... http://msdn.microsoft.com/en-us/library/ms186981%28v=sql.105%29.aspx
Seriously, normalize your table and if needed split it horizontally (put "theme grouped" columns inside separate table, to only read them when you need them).
EDIT: You can rewrite your query like this
select count(col_n) from tab
and if you want to get all columns at once (better):
SELECT
COUNT(column_1) column_1_count,
COUNT(column_2) column_2_count,
...
FROM table_name
If most records are not null maybe you can mix some of the approach suggested (for example check only nullable fields) with this:
if exists (select * from table where field is not null)
this should speed up the search because exists stops the search as soon as condition is met, in this example a single not null record is enough to decide the status of the field.
If the field has an index this should be almost instant.
Normally adding top 1 to this query is not needed because the query optimizer knows that you do not need to retrieve all the matching records.
You can use this stored procedure to the trick You need to provide the table name you wish to query note that if you'll pass to procedure the #exec parameter = 1 it will execute the select query
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[SP_SELECT_NON_NULL_COLUMNS] ( #tablename varchar (100)=null, #exec int =0)
AS BEGIN
SET NOCOUNT ON
IF #tablename IS NULL
RAISERROR('CANT EXECUTE THE PROC, TABLE NAME IS MISSING',16 ,1)
ELSE
BEGIN
IF OBJECT_ID('tempdb..#table') IS NOT NULL DROP TABLE #table
DECLARE #i VARCHAR (max)=''
DECLARE #sentence VARCHAR (max)=''
DECLARE #SELECT VARCHAR (max)
DECLARE #LocalTableName VARCHAR(50) = '['+#tablename+']'
CREATE TABLE #table (ColumnName VARCHAR (max))
SELECT #i+=
' IF EXISTS ( SELECT TOP 1 '+column_name+' FROM ' +#LocalTableName+' WHERE ' +column_name+
' '+'IS NOT NULL) INSERT INTO #table VALUES ('''+column_name+''');'
FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=#tablename
INSERT INTO #table
EXEC (#i)
SELECT #sentence = #sentence+' '+columnname+' ,' FROM #table
DROP TABLE #table
IF #exec=0
BEGIN
SELECT 'SELECT '+ LTRIM (left (#sentence,NULLIF(LEN (#sentence)-1,-1)))+
+' FROM ' +#LocalTableName
END
ELSE
BEGIN
SELECT #SELECT= 'SELECT '+ LTRIM (left (#sentence,NULLIF(LEN (#sentence)-1,-1)))+
+' FROM '+#LocalTableName
EXEC (#SELECT)
END
END
END
Use it like this:
EXEC [dbo].[SP_SELECT_NON_NULL_COLUMNS] 'YourTableName' , 1

Resources