TSQL to transform Address into a Mailing Address - SQL Server 2005 - sql-server

I would like to transform an Address (Line1, Line2, Line3, City, State, ZIP) into a Mailing Address (Addr1, Addr2, Addr3, Addr4) that has no blank lines and the City, State and ZIP are concatenated together on one line. Having a function do this would be very nice.
i.e.
Line1=
Line2=123 Somewhere
Line3=
City=Detroit
State=MI
Zip=48000
Here is the table stucture for the incoming address:
IF OBJECT_ID('tempdb..#Employee') IS NOT NULL DROP TABLE #Employee
CREATE TABLE #Employee (Line1 VARCHAR(30), Line2 VARCHAR(30), Line3 VARCHAR(30),
City VARCHAR(17), State VARCHAR(2), ZIP VARCHAR(10))
GO
INSERT #Employee VALUES ('', '123 Somewhere', '', 'Detroit', 'MI', '48000')
SELECT * FROM #Employee
The resulting Mailing Address
Addr1=123 Somewhere
Addr2=Detroit MI 48000
Addr3=
Addr4=
or one field with cr character
Addr=
123 Somewhere cr
Detroit MI 48000 cr
cr
cr
A function would be nice to return Addr1, Addr2, Addr3 and Addr4 or just Addr with .
SqueezeAddress(Line1, Line2, Line3, City, State, ZIP)
Then SqueezeAddress would return Addr1, Addr2, Addr3, Addr4
or
Addr with cr
All the Addr1-4 lines would be VARCHAR (40) or if one field is used Addr VARCHAR (200)
Per Phil's request in the comments below, here is the current logic that is being used (Many fields were removed to make it easier to read):
SELECT Line1, Line2, Line3,
ISNULL(LTRIM(RTRIM(ADDR.City)) + ', ','') + ISNULL(ADDR.RegionCode,'')
+ ' ' + ISNULL(ADDR.PostalCode,'') AS Line4,
UPDATE #tmpBilling
SET Line1 = Line2, Line2 = NULL
WHERE ISNULL(Line1, '') = ''
AND ISNULL(Line2, '') <> ''
UPDATE #tmpBilling
SET Line2 = Line3, Line3 = NULL
WHERE ISNULL(Line2, '') = ''
AND ISNULL(Line3, '') <> ''
UPDATE #tmpBilling
SET Line2 = Line4, Line4 = NULL
WHERE ISNULL(Line2, '') = ''
AND ISNULL(Line4, '') <> ''
UPDATE #tmpBilling
SET Line3 = Line4, Line4 = NULL
WHERE ISNULL(Line3, '') = ''
AND ISNULL(Line2, '') <> ''

I may be missing something here, but if this is just simple string concatenation, then this would work...
Set up testing data (I added a few more samples)
IF OBJECT_ID('tempdb..#Employee') IS NOT NULL DROP TABLE #Employee
CREATE TABLE #Employee (Line1 VARCHAR(30), Line2 VARCHAR(30), Line3 VARCHAR(30),
City VARCHAR(17), State VARCHAR(2), ZIP VARCHAR(10))
GO
INSERT #Employee VALUES ('', '123 Somewhere', '', 'Detroit', 'MI', '48001')
INSERT #Employee VALUES ('123 Somewhere', 'Suite 500', '', 'Detroit', 'MI', '48002')
INSERT #Employee VALUES ('123 Somewhere', 'Suite 500', 'attn: JP', 'Detroit', 'MI', '48003')
SELECT * FROM #Employee
From here, all you have to do is stitch the strings together. This version presumes that you have both nulls and empty strings to factor out.
SELECT
isnull(nullif(Line1, '') + char(13) + char(10), '')
+ isnull(nullif(Line2, '') + char(13) + char(10), '')
+ isnull(nullif(Line3, '') + char(13) + char(10), '')
+ City + ' ' + State + ' ' + ZIP
+ char(13) + char(10) + '------------------------------'
from #Employee
Wrap that into a function:
CREATE FUNCTION dbo.SqueezeAddress
(
#Line1 varchar(30)
,#Line2 varchar(30)
,#Line3 varchar(30)
,#City varchar(17)
,#State varchar(2)
,#Zip varchar(10)
)
RETURNS varchar(200)
AS
BEGIN
RETURN isnull(nullif(#Line1, '') + char(13) + char(10), '')
+ isnull(nullif(#Line2, '') + char(13) + char(10), '')
+ isnull(nullif(#Line3, '') + char(13) + char(10), '')
+ #City + ' ' + #State + ' ' + #ZIP
+ char(13) + char(10) + '------------------------------'
END
GO
Lastly, put the function in the query:
SELECT dbo.SqueezeAddress(Line1, Line2, Line3, City, State, Zip)
from #Employee

More straightforward (and easier to debug, IMHO):
-------------------------------------------------------------
-- assumptions:
--
-- * nullable fields never contain an nil (empty) string.
-- every nullable column will contain either a proper value
-- or NULL.
--
-- * zipcode is a 5- or 9-digit USPS zip code, without a dash.
-- Addresses lacking a zipcode will be NULL.
--------------------------------------------------------------
drop table dbo.address
go
create table dbo.address
(
id int not null identity(1,1) primary key clustered ,
line1 varchar(100) null ,
line2 varchar(100) null ,
line3 varchar(100) null ,
city varchar(100) null ,
state varchar(2) null ,
zipcode varchar(9) null ,
)
go
-----------------------------------------------------------------------
-- create a work table and rotate the source table such that
-- the work table contains 1 row for each non-null row for each address
-----------------------------------------------------------------------
drop table #addr
go
create table #addr
(
id int not null , -- pk.1
line_no int not null , -- pk.2
value varchar(100) not null ,
primary key clustered ( id , line_no ) ,
)
go
insert #addr ( id , line_no , value )
select addr.id , addr.line_no , addr.value
from ( select id = t.id ,
line_no = row_number() over ( partition by t.id order by t.seq ) ,
value = t.value
from ( select id = id ,
seq = 1 ,
value = line1
from dbo.address where line1 is not null
UNION
select id = id ,
seq = 2 ,
value = line2
from dbo.address where line2 is not null
UNION
select id = id ,
seq = 3 ,
value = line3
from dbo.address where line3 is not null
UNION
select id = id ,
seq = 4 ,
value = ltrim(rtrim(
coalesce( city , '' )
+ case when city is not null and state is not null then ', ' else '' end
+ coalesce( state , '' )
+ case when ( city is not null or state is not null ) and zipcode is not null then ' ' else '' end
+ coalesce( left(zipcode,5) , '' )
+ case when len(zipcode) = 9 then '-' + right(zipcode,4) else '' end
))
from dbo.address
where city is not null
OR state is not null
OR zipcode is not null
) t
) addr
---------------------------------------------------------------------
-- finally, do another table rotation to build the desired result set
---------------------------------------------------------------------
select id = addr.id ,
line1 = line1.value ,
line2 = line2.value ,
line3 = line3.value ,
line4 = line4.value
from #addr addr
left join #addr line1 on line1.id = addr.id and line1.line_no = 1
left join #addr line2 on line2.id = addr.id and line2.line_no = 2
left join #addr line3 on line3.id = addr.id and line3.line_no = 3
left join #addr line4 on line4.id = addr.id and line4.line_no = 4
order by addr.id

Assuming that empty values are actually NULL and not empty strings and that City, State and Zip are requried:
;With AddressValues As
(
Select PK, Line1 As LineValue, 1 As LinePos
From AddressTable
Union All
Select PK, Line2, 2
From AddressTable
Union All
Select PK, Line3, 3
From AddressTable
Union All
Select PK, [City] + ', ' + [State] + ' ' + [Zip], 4
From AddressTable
)
, OrderedValues As
(
Select PK, LineValue
, Row_Number() Over( Partition By PK Order By LinePos ) As Num
From AddressValues
Where LineValue Is Not Null
)
, ValuesAsColumns As
(
Select PK
, Case When Num = 1 Then LineValue End As Line1
, Case When Num = 2 Then LineValue End As Line2
, Case When Num = 3 Then LineValue End As Line3
, Case When Num = 4 Then LineValue End As Line4
From OrderedValues
Group By PK
)
Update #tmpBilling
Set Line1 = VC.Line1
, Line2 = VC.Line2
, Line3 = VC.Line3
, Line4 = VC.Line4
From #tmpBilling As B
Join ValuesAsColumns As VC
On VC.PK = B.PK
EDIT
Here is the same result in the form of a function:
CREATE FUNCTION dbo.SqueezeAddress
(
#Line1 varchar(50)
, #Line2 varchar(50)
, #Line3 varchar(50)
, #City varchar(50)
, #State varchar(50)
, #Zip varchar(50)
, #LineNumToReturn int
)
RETURNS varchar(50)
AS
BEGIN
Declare #Result varchar(50);
With AddressValues As
(
Select #Line1 As LineValue, 1 As LinePos
Union All
Select #Line2, 2
Union All
Select #Line3, 3
Union All
Select #City + ', ' + #State + ' ' + #Zip, 4
)
, OrderedValues As
(
Select LineValue
, Row_Number() Over( Order By LinePos ) As Num
From AddressValues
Where LineValue Is Not Null
)
Select #Result = LineValue
From OrderedValues
Where Num = #LineNumToReturn
Return #Result
END
GO
Select dbo.SqueezeAddress(null, '123 Main St', null, 'Detroit', 'MI', '12345', 1)
, dbo.SqueezeAddress(null, '123 Main St', null, 'Detroit', 'MI', '12345', 2)
, dbo.SqueezeAddress(null, '123 Main St', null, 'Detroit', 'MI', '12345', 3)
, dbo.SqueezeAddress(null, '123 Main St', null, 'Detroit', 'MI', '12345', 4)
)

Related

Performance tuning on matching word using CHARINDEX and COLLATION

I have two tables with following records:
Table 1: 100 rows
Table 2: 50 Millions rows
Example:
Table 1: tb100
create table tb100
(
name varchar(50)
);
insert into tb100 values('Mak John'),('Will Smith'),('Luke W')......100 rows.
Table 2: tb50mil
create table tb50mil
(
name varchar(50)
);
insert into tb10mil values('John A Mak'),('K Smith Will'),('James Henry')......50 millions rows.
create nonclustered index nci_tb10mil_name on tb10mil(name);
Requirement: I want to match the name between two tables, if any WORD(John,Smith,Will) present in another table. For example John present in John A Mark.
My try: Used XML to split column name of the table tb100 and also adding collation with CHARINDEX.
;WITH splitdata AS
(
SELECT splitname
FROM
(
SELECT *,Cast('<X>' + Replace(t.name, ' ', '</X><X>') + '</X>' AS XML) AS xmlfilter
FROM tb100 t
)F1
CROSS apply
(
SELECT fdata.d.value('.', 'varchar(50)') AS splitName
FROM f1.xmlfilter.nodes('X') AS fdata(d)
) O
)
SELECT t2.name AS [Aadhar Names]
FROM tb50mil t2
INNER JOIN splitdata S
ON CHARINDEX(S.splitname collate Latin1_General_BIN,T2.name collate Latin1_General_BIN)>0
GROUP BY t2.name
Time taken to execution: 00:01:34
Rows affected: (2251429 row(s) affected)
Execution Plan:
If you need the separate words within your name then maybe having a table where your name is just one string isn't optimal conceptionally. Also, separating the names now is painful since you have no reoccurring pattern to your middle names. Plus string modification is really not a SQL strength. I would instead extend your table into something like this:
alter table tb100
add
nameID int IDENTITY(1,1) NOT NULL,
first_name varchar(50) null,
middle_name varchar(50) null,
last_name varchar(50) null
insert into tb100 values('Mak John'),('Will Smith'),('Luke W')......100 rows.
if (SELECT LEN(col) - LEN(REPLACE(col, ' ', '')) > 1)
update tb100
set
first_name = (Select Substring(name, 0, (Charindex(' ', name)))),
middle_name = (Select Substring( Right(name, (LEN(name) - (Charindex(' ', name) +1)), 0, (Charindex(' ', name)))),
last_name = (select Substring( Right(middle_name, (LEN(middle_name) - (Charindex(' ', middle_name) + 1)), LEN(LEN(middle_name) - (Charindex(' ', middle_name) + 1))
else
update tb100
set
first_name = (Select Substring(name, 0, (Charindex(' ', name)))),
middle_name = '',
last_name = (Select Substring( Right(name, (LEN(name) - (Charindex(' ', name) +1)), 0, (Charindex(' ', name))))
I hope it works I didn't have a chance to test it because I'm on the road.
If you have the chance of inserting the data into those columns without this entire modification then please do so.
You then do the same to your other table...
alter table tb50mil
add
nameID int IDENTITY(1,1) NOT NULL,
first_name varchar(50) null,
middle_name varchar(50) null,
last_name varchar(50) null
insert into tb10mil values('John A Mak'),('K Smith Will'),('James Henry')......50 million rows.
if (SELECT LEN(col) - LEN(REPLACE(col, ' ', '')) > 1)
update tb50mil
set
first_name = (Select Substring(name, 0, (Charindex(' ', name)))),
middle_name = (Select Substring( Right(name, (LEN(name) - (Charindex(' ', name) +1)), 0, (Charindex(' ', name)))),
last_name = (select Substring( Right(middle_name, (LEN(middle_name) - (Charindex(' ', middle_name) + 1)), LEN(LEN(middle_name) - (Charindex(' ', middle_name) + 1))
else
update tb50mil
set
first_name = (Select Substring(name, 0, (Charindex(' ', name)))),
middle_name = '',
last_name = (Select Substring( Right(name, (LEN(name) - (Charindex(' ', name) +1)), 0, (Charindex(' ', name))))
and from here on it's a simple join really:
select * from tb100 hun
inner join
tb50mil mil on hun.first_name = mil.first_name OR hun.middle_name = mil.middle_name OR hun.last_name OR mil.last_name
Hope this helps!

Compare Identical Columns of 2 Different Table for Mismatch Data

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

Fill with spaces a column value in update

How to automatically fill a column with spaces to a pre-determined length in update SQL sentence in SQL Server 2012?
I have a table with several columns like
Col1 NVARCHAR(10)
Col2 NVARCHAR(100)
Col3 NVARCHAR(200)
Col4 NVARCHAR(50)
and more.
If value of column is NULL or '', I update the column with spaces to a pre-determined length (the lenth of the column).
For Col3, if value is NULL or '', spaces to 200 blank space (' ')
if value has any characters, 'abcd', fill (pad right) to 200 blank spaces. Then, finally 4 not spaces characters and 196 spaces characteres.
For example, for Col1 has length 10.
1) Value = NULL , Col1 value = ' ' (10 spaces)
2) Value = '' , Col1 value = ' ' (10 spaces)
2) Value = 'abc' , Col1 value = 'abc ' (abc and 7 spaces)
How can I do that in the UPDATE SQL?
Maybe using
select column_name, data_type, character_maximum_length
from information_schema.columns
where table_name = 'myTable'
or
SELECT COL_LENGTH('Table', 'Column')
More in How to get the size of a varchar[n] field in one SQL statement?
Try the following, the LEFT is used to keep the length down to the column length, while the space ensures the field is filled with spaces:
create table test (col1 varchar(10), col2 varchar(15))
GO
insert into test (col1, col2)
values ('', '')
,(NULL, NULL)
,('abc', 'abc')
UPDATE test
SET col1 = LEFT(COALESCE(col1, '') + SPACE(COL_LENGTH('test', 'col1')), COL_LENGTH('test', 'col1'))
,col2 = LEFT(COALESCE(col2, '') + SPACE(COL_LENGTH('test', 'col2')), COL_LENGTH('test', 'col2'))
FROM test
SELECT *
FROM test
I don't understand what you want exactly, but here is what I understand:
CREATE TABLE MyTable (
Col1 NVARCHAR(200),
Col2 NVARCHAR(100),
Col3 NVARCHAR(200),
Col4 NVARCHAR(50)
);
INSERT INTO MyTable VALUES (NULL, NULL, NULL, NULL), ('ABC', NULL, NULL, NULL);
-- You can do the same for the other cols
UPDATE MyTABLE
SET Col1 = REPLICATE(' ', COL_LENGTH('MyTable', 'Col1')/2)
WHERE Col1 IS NULL;
SELECT *
FROM MyTable;
Demo
Update:
Here is how to do it in one statement:
UPDATE MyTABLE
SET Col1 = (SELECT CASE WHEN (Col1 IS NULL) OR (Col1 = '') THEN
REPLICATE(' ', COL_LENGTH('MyTable', 'Col1')/2)
ELSE Col1 + REPLICATE(' ', (COL_LENGTH('MyTable', 'Col1')/2)- LEN(Col1)) END),
Col2 = (SELECT CASE WHEN (Col2 IS NULL) OR (Col2 = '') THEN
REPLICATE(' ', COL_LENGTH('MyTable', 'Col2')/2)
ELSE Col2 + REPLICATE(' ', (COL_LENGTH('MyTable', 'Col2')/2)- LEN(Col2)) END),
Col3 = (SELECT CASE WHEN (Col3 IS NULL) OR (Col3 = '') THEN
REPLICATE(' ', COL_LENGTH('MyTable', 'Col1')/2)
ELSE Col3 + REPLICATE(' ', (COL_LENGTH('MyTable', 'Col3')/2)- LEN(Col3)) END),
Col4 = (SELECT CASE WHEN (Col4 IS NULL) OR (Col4 = '') THEN
REPLICATE(' ', COL_LENGTH('MyTable', 'Col4')/2)
ELSE Col4 + REPLICATE(' ', (COL_LENGTH('MyTable', 'Col4')/2)- LEN(Col4)) END);
Demo

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 get column names from a table and count not null values of them in SQL Server?

I tried something like this, which did not work:
SELECT COLUMN_NAME, COUNT(COLUMN_NAME) AS COUNT_NOT_NULL FROM myDB.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTable';
Obviously, the "information-meta-table" doesn't have the data from myTable to count. So do I need some sort of JOIN? Do I need to declare some variable and use some iteration?
The weird part is that this solution works for some tables, but not for others (it counts NULLs): https://stackoverflow.com/a/24411738/8055476
First I created a table with some sample data
IF OBJECT_ID('COuntNotNull') IS NOT NULL
Drop Table COuntNotNull
;With cte(Column1 , Column2 , Column3 , Column4 )
AS
(
SELECT 'X' , ' X' , NULL , 'X' Union all
SELECT NULL , NULL , 'X' , 'X' Union all
SELECT NULL , NULL , NULL , NULL
)
SELECT * INTO COUNTNOTNULL FROM cte
The below code get the column names dynamically For given table and get count of non null values
DECLARE #DynamicColms NVARCHAR(max)
,#CaseDynamicColms NVARCHAR(max)
,#Sql NVARCHAR(max)
,#TableName VARCHAR(100) = 'COuntNotNull'--Here give Your TableName
SELECT #DynamicColms = STUFF((
SELECT ', ' + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
FOR XML PATH('')
), 1, 1, '')
--SELECT #DynamicColms
SELECT #CaseDynamicColms = STUFF((
SELECT '+ ' + 'CASE WHEN ' + COLUMN_NAME + ' IS NOT NULL THEN 1 ELSE 0 END'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
FOR XML PATH('')
), 1, 1, '')
--SELECT #CaseDynamicColms
SET #Sql = 'SELECT ' + #DynamicColms + ',' + #CaseDynamicColms + CHAR(13) + CHAR(10) + ' AS COUNT_NOT_NULL FROM ' + #TableName
PRINT #Sql
EXECUTE (#Sql)
Result
Column1 Column2 Column3 Column4 COUNT_NOT_NULL
-------------------------------------------------
X X NULL X 3
NULL NULL X X 2
NULL NULL NULL NULL 0

Resources