T SQL Convert a row to a string - sql

Is there a valid way in SQL Server 2005 to read a row of data into a string?
For example if my table looked like the following:
ID | First Name | Last Name | Color
-----------------------------------
1 | Timmy | Jones | pink
2 | Martha | Fisher | green
That I could get back as a result:
'1 Timmy Jones pink'
'2 Martha Fisher green'
Preferably I could get back with the column name appended such as:
'ID: 1, First Name: Timmy, Last Name: Jones, Color: pink'
'ID: 2, First Name: Martha, Last Name: Fisher, Color: green'
Update:
I'm looking for a dynamic solution so I guess querying against INFORMATION_SCHEMA would be more appropriate.
This actually helps a bit because I'm looking to set it off from an update trigger, so I'll have one row at a time and I can query against the INFORMATION_SCHEMA for the main table checking datatypes to eliminate text, ntext and image that the generated tables, 'inserted' and 'deleted' choke on.
Solution: (updated: had to add some more code to return the one value)
-- passed in table name & schema
DECLARE #table_name nvarchar(100)
DECLARE #schema_name nvarchar(100)
-- set initial value
SET #table_name = 'my_table'
SET #schema_name = 'dbo'
-- Variable to hold query
DECLARE #sql nvarchar(MAX)
SET #sql = 'SELECT #row_stringOUT = '
-- Query columns meeting criteria and build query
SELECT
-- Query as one string
#sql = (#sql + ' '' ' + COLUMN_NAME + ': '' + CASE WHEN ' + COLUMN_NAME + ' IS NULL THEN '''' WHEN ISDATE(' + COLUMN_NAME + ') = 1 THEN CONVERT(nvarchar(100), ' + COLUMN_NAME + ', 120) ELSE CAST(' + COLUMN_NAME + ' AS nvarchar(100)) END + ')
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
table_schema = #schema_name
AND
table_name = #table_name
AND
-- Filter out unuseable data types
data_type NOT IN ('text', 'ntext', 'image')
-- Trim extra character before FROM statement then add FROM statement
SELECT #sql = LEFT(#sql, LEN(#sql) - 1) + ' FROM ' + #table_name;
-- Execute Query
DECLARE #params nvarchar(MAX)
SET #params = N'#row_stringOUT ntext OUTPUT';
DECLARE #ret_value nvarchar(MAX)
EXEC sp_executesql
#sql
, #params
, #row_stringOUT = #ret_value OUTPUT
SELECT #ret_value

you can just concat the columns. if you want to get column names to you'll have to use some mix of dynamic sql, INFORMATION_SCHEMA views and querying...
SELECT cast(ID as varchar(10)) + ' ' + [First Name] + ' ' + [Last Name] + ' ' + Color
FROM MyTable

Select CAST(ID As Varchar(8)) + ' ' + [First Name] + ' ' + [Last Name] + ' ' + Color
From TheTable
To get the verbose formatting you mentioned:
Select 'ID: ' + CAST(ID As Varchar(8)) + ', First Name: ' + [First Name] + ', Last Name: ' + [Last Name] + ', Color: ' + Color
From TheTable
Edit: Now assuming ID is an integer autonumber. Most likely the case, as ajdams points out.

Actually if ID has a data type of int the solution Patrick suggested will give you a conversion error. You will have to do something like this
SELECT CAST(ID AS varchar(10)) + ' ' + [first name] + ' ' + [last name + ' ' + color
FROM MYTABLE

Related

Is there a way to Replace(*) in sql?

So simply I'm doing something similar to:
select
[BadData], [WorseDate], [IQuitData]
into
#BadDataTempTable
from
SomeoneElsesMess
what I want to do now is something similar to:
Select
Replace(#BadDataTempTable.*, ',', ' ')
from
#BadDataTempTable -- Replace all commas in every column `with a space--`
Is this possible? If so please show me the easiest (non-function) way to do so.
Thanks. SQL Server 2012 I think. I'm using SSMS 17
No, the columns have to be specified. You could use dynamic SQL to build your update / query. Then just copy the command you want from the results.
Maybe this will help get you started:
BEGIN
-- Set the replace value
DECLARE #ls_replaceValue NVARCHAR(MAX) = ',';
-- Set the with value
DECLARE #ls_withValue NVARCHAR(MAX) = ' ';
-- Set the table name we want to query
DECLARE #ls_table NVARCHAR(MAX) = 'some_table';
-- Get all of the columns and provide the replace parameters
DECLARE #ls_columns NVARCHAR(MAX) = '';
SELECT #ls_columns = #ls_columns + ', ' + name + ' = REPLACE(' + name + ', ' + '' + '''' + REPLACE(#ls_replaceValue, '''', '''''''') + '''' + ', ' + '''' + REPLACE(#ls_withValue, '''', '''''''') + '''' + ')'
FROM sys.all_columns
WHERE object_id = OBJECT_ID(#ls_table)
AND collation_name IS NOT NULL; -- Skip columns that aren't character based
-- Remove the first ', ' from the column list
SET #ls_columns = SUBSTRING(#ls_columns, 3, LEN(#ls_columns));
IF #ls_columns = ''
BEGIN
PRINT 'Table not found'
RETURN
END
-- Build a query
DECLARE #ls_query_sql NVARCHAR(MAX) = '';
SET #ls_query_sql = 'SELECT ' + #ls_columns + ' FROM ' + #ls_table;
-- Show the results
SELECT #ls_query_sql AS querySQL;
END
Just since the OP asked about how you might do this in dynamic SQL, here's how I'd approach it. Basically get the table schema information and concatenate all the columns, plus the REPLACE logic you want using FOR XML. This basically constructs the statement Rigerta posted, but does it dynamically.
use tempdb
go
if object_id('tempdb.dbo.#SomeoneElsesBadData') is not null drop table #SomeoneElsesBadData
create table #SomeoneElsesBadData
(
BadData varchar(250),
WorseData varchar(250),
IQuitData varchar(250)
)
declare #sql nvarchar(max)
select #sql = 'select '
+ stuff((select ', '
+ name
+ ' = replace(' + name + ''','', '''')'
from tempdb.sys.columns
where object_id = object_id('tempdb.dbo.#SomeoneElsesBadData')
for xml path('')), 1, 1, '')
+ ' into #BadDataTempTable
from #SomeoneElsesBadData'
exec sp_executesql #sql
All things being equal, the data should probably be cleaned before it gets into SQL, but reality is rarely fair.

How to convert horizontal row into vertical

I have a Dynamic query which fetch a row with 62 columns in sql
The query is like this
DECLARE #SQLQuery AS nvarchar(max)
DECLARE #columns nvarchar(max)
SELECT
#columns = ISNULL(#columns + ', ', '') + QUOTENAME(Column_name)
FROM (SELECT
Column_name
FROM
(SELECT
Column_name,
ROW_NUMBER() OVER (PARTITION BY Column_name ORDER BY Column_name) AS Rows
FROM information_Schema.Columns
WHERE Table_Name IN ('Parking', 'AIV_Parking')
AND column_name NOT IN ('IssueNo', 'Issuedate', 'Agency', 'OfficerName', 'OfficerId', 'Beat', 'UNITSERIAL', 'VEHLICNO', 'VEHLICSTATE', 'VEHLICEXPDATE', 'VEHLICTYPE', 'VEHMAKE',
'VEHMODEL', 'VEHBODYSTYLE', 'VEHVIN4', 'VEHVIN', 'ISSUENOCHKDGT', 'VEHCOLOR1', 'VEHCOLOR2', 'PERMITNO', 'VEHLABELNO', 'LOCBLOCK', 'LOCSTREET', 'LOCDESCRIPTOR', 'LOCTRAVELDIR', 'LOCSIDEOFSTREET',
'LOCSUBURB', 'LOCLOT', 'LOCCROSSSTREET1', 'LOCCROSSSTREET2', 'LOCSTATE', 'LOCPOSTALCODE', 'METERNO', 'METERBAYNO', 'REMARK1', 'REMARK2')) p
WHERE Rows = 2) RequiredColumns
EXEC ('SELECT ' + #columns + ' FROM AIV_Parking Where Issueno =''100000600''')
Which gives me below records
So result is Something like below where i get only 1 row at a time.
|ACTLINE1 | ACTLINE2 | AUTOPROC_UNIQUEKEY | COURTADDR| IssueNo|.......
|SUBIACO | LOCAL LAWS| NULL | NULL | 123 |
Now i want this to converted like:
|FieldName | FieldValue
|ACTLINE1 | SUBIACO
|ACTLINE2 | LOCAL LAWS
|ISSUENO | 123
While pivoting these column ,I want only those columns which has value i don't want those column whose value is null or ' '
You need to use UNION ALL and filter the result with a WHERE clause:
DECLARE #SQLQuery AS NVARCHAR(MAX) = ''
SELECT #SQLQuery = #SQLQuery +
'SELECT ''' + COLUMN_NAME + ''' AS FieldName, CAST(' + QUOTENAME(COLUMN_NAME) + ' AS NVARCHAR(MAX)) AS FieldValue
FROM AIV_Parking
WHERE Issueno =''100000600''
UNION ALL' + CHAR(10)
--QUERY HERE TO GET COLUMNS FROM INFORMATION_SCHEMA.COLUMNS
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME IN('Parking','AIV_Parking')
IF #SQLQuery <> '' BEGIN
-- Remove the last occurence of UNION ALL
SELECT #SQLQuery = LEFT(#SQLQuery, LEN(#SQLQuery) - 11)
SELECT #SQLQuery =
'SELECT * FROM (' + #SQLQuery + ') t WHERE FieldValue IS NOT NULL AND FieldValue <> '''''
EXEC(#SQLQuery)
END
Note that you need to CAST your FieldValue to a NVARCHAR(MAX) or whatever datatype that will not produce a conversion error. This is because when using UNION all columns must have the same datatype, if not, they will be converted to the one with the higher data type precedence.

How to search about a specific value in all columns in the table?

I have a table (students_all) that contains 4 columns as below :-
student_name
age
student_id
class
Now how can I search about a specific value such as 'left' in all columns in one query using SQL Server 2008.
I mean how can make something like Excel (Ctrl+F) to find any value in all columns.
There are many ways to check to find something like that in one SQL for a special table, So I suggest this way:
SELECT *
FROM students_all
WHERE student_name + age + student_id + class LIKE '%left%';
But for a dynamic way I use this:
DECLARE #TableName nvarchar(256) = 'Table_1';
DECLARE #Find nvarchar(50) = '1';
DECLARE #sql nvarchar(max) = '';
SELECT #sql = #sql +
CASE
WHEN #sql = '' THEN ''
ELSE '+ '
END + 'CONVERT(nvarchar(max), ISNULL(' + cols.COLUMN_NAME + ', '''')) COLLATE DATABASE_DEFAULT '
FROM INFORMATION_SCHEMA.COLUMNS cols
WHERE cols.TABLE_NAME = #TableName;
SELECT #sql = 'SELECT * FROM ' + #TableName + ' WHERE ' + #sql + ' LIKE ''%' + #Find + '%''';
-- Or if you want to add other field types you can use this instead:
--SELECT #sql = 'SELECT * FROM ' + #TableName + ' WHERE CAST(' + #sql + ' AS NVARCHAR(MAX)) LIKE ''%' + #Find + '%''';
EXEC(#sql);
SELECT *
FROM students_all
WHERE 'left' in (student_name , age ,student_id , class);
It will provide you the row where Left is present
SELECT *
FROM students_all as t
WHERE t.student_name == 'left'
OR t.age == 'left'
OR t.student_id == 'left'
OR t.class == 'left'
There isn't a "fast" way to check every colums at one time, you need to check 1by1 every colum!
What i understand is that u want data which have value left in any of column of table
For that you can use
SELECT *
FROM students_all
WHERE
student_name == 'left'
OR age == 'left'
OR student_id == 'left'
OR class == 'left'
This will check give you the rows which have at least one column of value left
But i you want to get data which contain the word left the you need to change all those for conditions in WHERE to COLULMN_NAME LIKE %left%
Hopefully this will help.
I think this earlier post is what the OP's trying to refer to.
It requires modification, however, to search only from 1 specific table.
SELECT * FROM students_all as std
WHERE
std.student_name = 'left'
OR std.age = 'left'
OR std.student_id = 'left'
OR std.class = 'left'
Modifying #shA.t's answer to allow for column names with spaces.
DECLARE #TableName nvarchar(256) = 'TableName';
DECLARE #Find nvarchar(50) = 'SearchText';
DECLARE #sql nvarchar(max) = '';
SELECT #sql = #sql +
CASE
WHEN #sql = '' THEN ''
ELSE ' OR '
END + '[' + cols.COLUMN_NAME +']' + ' LIKE ''%' + #Find +'%'''
FROM INFORMATION_SCHEMA.COLUMNS cols
WHERE cols.TABLE_NAME = #TableName;
select #sql = 'SELECT * FROM ' + #TableName + ' WHERE ' + #sql
execute(#sql)
select * from students_all where left (
student_name
age
student_id
class
if all the columns are string type

Return a percentage of NULL values for a specific record

The following query returns the values of the table for each field in terms of null percentage . What I want is to get the sum of those percentages for a specific ProductID. Also, I would like to get a percentage (in an extra column) of the fields do not have value i.e. ="". Any ideas?
use AdventureWorks
DECLARE #TotalCount decimal(10,2), #SQL NVARCHAR(MAX)
SELECT #TotalCount = COUNT(*) FROM [AdventureWorks].[Production].[Product]
SELECT #SQL =
COALESCE(#SQL + ', ','SELECT ') +
'cast(sum (case when ' + QUOTENAME(column_Name) +
' IS NULL then 1 else 0 end)/#TotalCount*100.00 as decimal(10,2)) as [' +
column_Name + ' NULL %]
'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Product' and TABLE_SCHEMA = 'Production'
SET #SQL = 'set #TotalCount = NULLIF(#TotalCount,0)
' + #SQL + '
FROM [AdventureWorks].Production.Product'
print #SQL
EXECUTE SP_EXECUTESQL #SQL, N'#TotalCount decimal(10,2)', #TotalCount
You can use the following:
use AdventureWorks
DECLARE #colCount int;
DECLARE #nullCheck nvarchar(max) = N'';
DECLARE #emptyCheck nvarchar(max) = N'';
DECLARE #SQL NVARCHAR(MAX);
DECLARE #KeyToCheck int = 123; -- adapt as necessary
SELECT
#nullCheck += '
+ ' + 'count(' + QUOTENAME(column_Name) + ')'
,#emptyCheck += '
+ ' +
CASE
WHEN DATA_TYPE IN('bigint', 'int', 'smallint', 'tinyint', 'bit', 'money', 'smallmoney', 'numeric', 'decimal', 'float', 'real') THEN
-- check numeric data for zero
'sum(case when coalesce(' + QUOTENAME(column_Name) + ', 0) = 0 then 1 else 0 end)'
WHEN DATA_TYPE like '%char' or DATA_TYPE like '%text' THEN
--check character data types for empty string
'sum(case when coalesce(' + QUOTENAME(column_Name) + ', '''') = '''' then 1 else 0 end)'
ELSE -- otherwise, only check for null
'sum(case when ' + QUOTENAME(column_Name) + ' IS NULL then 1 else 0 end)'
END
,#colCount =
count(*) over()
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Product' and TABLE_SCHEMA = 'Production'
;
SET #SQL = 'SELECT case when count(*) > 0 then 100.00 - (' + #nullCheck + '
) * 100.00 / ' + cast(#colCount as nvarchar(max)) + '.00 / count(*) end as null_percent
, case when count(*) > 0 then (' + #emptyCheck + '
) * 100.00 / ' + cast(#colCount as nvarchar(max)) + '.00 / count(*) end as empty_percent
FROM Production.Product
WHERE ProductID = ' + cast(#KeyToCheck as nvarchar(max))
;
print #SQL;
EXECUTE (#SQL)
I simplified one of your expressions: Instead of sum (case when <column> IS NULL then 1 else 0 end), you can just use count(<column>). When using count with an expression instead of *, it counts the rows where this expression is non-null. As this is the opposite from what you need, I added the 100.00 - as the start of the SELECT.
For the "empty check", this would make the logic more complex to understand, hence I left the original logic there and extended it. There, I implemented an check for emptiness for numeric and character/text data types. You can easily extend that for date, binary data etc. with whichever logic you use to determine if a column is empty.
I also found it more simple to leave first + in the two variables #nullCheck and #emptyCheck, as it is valid SQL to start an expression wit this.
I also extended the statement so that if there would potentially be more than one record with ProductId = 123, it shows the average across all records, i. e. the total sum divided by the count of rows. And the outermost case expressions just avoid an division by zero error if count(*) would be zero, i. e. no record with ProductId = 123 found. In that case the return value is null.
You could use AVG function:
SELECT AVG(CASE WHEN value IS NULL THEN 100 ELSE 0 END) AS Percents
FROM Table
UPDATE:
Here is your script:
DECLARE #SQL NVARCHAR(MAX), #TABLE_NAME NVARCHAR(MAX), #TABLE_SCHEMA NVARCHAR(MAX), #PK NVARCHAR(MAX)
SET #TABLE_NAME = 'tblBigTable'
SET #TABLE_SCHEMA = 'dbo'
SET #PK = '8'
SELECT
#SQL = COALESCE(#SQL + ', ', 'SELECT ') +'AVG(CASE WHEN ' + COLUMN_NAME + ' IS NULL THEN 100 ELSE 0 END) AS [' + COLUMN_NAME +' NULL %]'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = #TABLE_SCHEMA AND
TABLE_NAME = #TABLE_NAME
SET #SQL = #SQL + ' FROM ' + #TABLE_NAME + ' WHERE pkId = ''' + #PK + ''''
print #SQL
EXECUTE SP_EXECUTESQL #SQL

add created column to search critera?

below is my search table proecedure that searches all columns. Ive added the CleanName column on the fly and need that be searchable in the other query, reason being if i currently search for "bob smith" im getting 0 matches but bob returns matches becuase of first name, so in this example cleanname is actaully full name
USE [ITAPP]
GO
/****** Object: StoredProcedure [dbo].[sp_SearchAllTables] Script Date: 07/11/2013 10:57:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[sp_SearchAllTables]
(
#SearchStr nvarchar(255)
)
AS
BEGIN
declare #where varchar(8000)
declare #sql varchar(8000)
set #sql = 'select u.*,e.*, (u.Forename + '' '' + u.Surname) AS CleanName from tblUsers u join tblEquipment e on e.userid = u.id WHERE 1 = 1 AND ( 1= 0 '
select #where = coalesce(#where ,'' ) + ' OR ' + case when object_name(object_id) = 'tblUsers' then 'u' else 'e' end + '.[' + name + '] LIKE ''%' + replace(#SearchStr, '''', '''''') + '%'' '
from sys.columns where object_id in ( select object_id from sys.objects where name in ( 'tblUsers','tblEquipment' ))
and collation_name is not null
set #where = coalesce(#where, '') + ')'
print #sql
print #where
exec(#sql + #where)
END
No, you can only use columns from the source (or expressions containing those columns) in the WHERE clause
normally you'd add it to the WHERE clause this way:
WHERE (u.Forename + '' '' + u.Surname) LIKE '%' + #SearchStr + '%'
but doing that with the method you've chosen may not be possible.
adding this line
select #where = ' OR u.[Forename] + '' '' + u.[Surname] LIKE ''%' + replace(#SearchStr, '''', '''''') + '%'''
after the set statement fixed my issues