Concatenate rows into a single text string - sql

I have seen similar questions but feel this is not a duplicate question. I would like to concatenate rows into single string with joins. I am confused on how to proceed. Syntax below
I am getting an error "There is already an object named '#TEMPTABLE' in the database." also I am not sure if my sytax to the 2nd select statement is correct, help?
SELECT DISTINCT DisplayName, addrSt, addrCntyName,
RIGHT('00' + CONVERT(varchar, addrStFips), 2) + RIGHT('000' + convert(varchar, addrCntyFips), 3) AS addrFips
INTO #TEMPTABLE
FROM PPP
INNER JOIN poa ON PPP.OAJ = poa.OAJ
INNER JOIN dcPfp ON PfpPayor.KEYF = dbo.dcPfp.KEYJ
INNER JOIN ProvOff ON ProvOffAfl.OJK = ProvOff.OJK
SELECT DISTINCT
addrFips,
STUFF
(
(
SELECT ',' + DisplayName
FROM #TEMPTABLE M
WHERE M.addrFips = B.addrFips
ORDER BY DisplayName
FOR XML PATH('')
),1,1,''
) AS DISPLAYNAMES
FROM #TEMPTABLE B
DROP TABLE #TEMPTABLE

The error message There is already an object named '#TEMPTABLE' in the database. means that you've created a temporary table with this name in a previous run. To solve this, you need to DROP this table or use another name.
To drop, use the following code before your query:
IF OBJECT_ID('tempdb..#TEMPTABLE') IS NOT NULL
DROP TABLE #TEMPTABLE

What worked in my situation was IF OBJECT_ID('tempdb.#TEMPTABLE') IS NOT NULL DROP TABLE #Results
GO
Thank you all!

You can use undocumented string concatenation feature.
DECLARE #big_string varchar(max) = '';
SELECT #big_string += x.s + ','
FROM (VALUES ('string1'), ('string2'), ('string3')) AS x(s);
SELECT #big_string;
-- OUTPUT:
-- string1,string2,string3,
It's that easy. As for order guarantees, read this article.

Related

How to put a comma separated value from a column in a table into SQL IN operator?

I have a table which has a column in which I am storing a comma separated text with single quotes for each of the comma separated values. These values are employee IDs. This is how it looks
Now, I have a SQL query wherein I need to put the value from this column into a SQL IN operator. Something like this:
select *
from EMPLOYEE_MASTER
where EMPLOYEEID IN (select CM_CONFIG_VALUE
from ADL_CONFIG_MAST_T
where CM_CONFIG_KEY like 'ATT_BIOMETRIC_OU_ID'
)
But this, does not work, the query when executed returns 0 rows whereas if I execute the query normally like below, it works.
select *
from EMPLOYEE_MASTER
where EMPLOYEEID IN('9F3DD4B791554DDE','C9B90D62851D43AB','828CB9E6204B4DDC')
Please suggest what I should do here. I have tried using substring to remove the first and the last character as well assuming that single quotes might be the issue, but that does not work either.
select * from EMPLOYEE_MASTER where EMPLOYEEID IN(select EMPLOYEEID from ADL_CONFIG_MAST_T where CM_CONFIG_KEY like 'ATT_BIOMETRIC_OU_ID')
column should be same in where COLUMNNAME IN (select COLUMNNMAE from tablename)
You can create a temp varible and then use exec command to get the desired result.
declare #temp varchar(200)
select #temp=CM_CONFIG_VALUE
from ADL_CONFIG_MAST_T
where CM_CONFIG_KEY like 'ATT_BIOMETRIC_OU_ID'
exec('select *
from EMPLOYEE_MASTER
where EMPLOYEEID IN (' + #temp + ')')
Try This:
DECLARE #ID VARCHAR(500);
DECLARE #Number VARCHAR(500);
DECLARE #comma CHAR;
SET #comma = ','
SET #ID = (select CM_CONFIG_VALUE
from ADL_CONFIG_MAST_T
where CM_CONFIG_KEY like %ATT_BIOMETRIC_OU_ID% + #comma);
Create table #temp (EMPLOYEEID varchar(500))
WHILE CHARINDEX(#comma, #ID) > 0
BEGIN
SET #Number = SUBSTRING(#ID, 0, CHARINDEX(#comma, #ID))
SET #ID = SUBSTRING(#ID, CHARINDEX(#comma, #ID) + 1, LEN(#ID))
Insert into #temp
select #Number
END
select *
from EMPLOYEE_MASTER
where EMPLOYEEID IN(select EMPLOYEEID from #temp)
The reason you are not getting it in your query is because your inner query returns only one row. So your query searches for '9F3DD4B791554DDE','C9B90D62851D43AB','828CB9E6204B4DDC' as as single record.
If your compatibility level is greater than or equal to 130 you can use STRING_SPLIT() function. Then your query would be
SELECT *
FROM EMPLOYEE_MASTER
WHERE EMPLOYEEID IN
(SELECT value AS empid
FROM ADL_CONFIG_MAST_T CROSS APPLY string_split(CM_CONFIG_VALUE, ',' )
WHERE CM_CONFIG_KEY LIKE 'ATT_BIOMETRIC_OU_ID' )
What this actually does is, it splits the CM_CONFIG_VALUE with ',' and returns them as rows. This is the value column I have referred. Then you use them with the IN clause.
Hope this helps!
Direct IN condition will not work here. You have split your string before searching. You can do that with XML options in SQL SERVER 2014
SELECT *
FROM EMP
WHERE EMPID IN (
SELECT a.c.value('.', 'VARCHAR(1000)')
FROM (
SELECT x = CAST('<a>' +
REPLACE(REPLACE(CM_CONFIG_VALUE , ',', '</a><a>'),'''','') + '</a>' AS XML )
FROM ADL_CONFIG_MAST_T
-- WHERE <your_condition>
) m
CROSS APPLY x.nodes('/a') a(c))
CHECK DEMO HERE
For the version 2016 and above you can use STRING_SPLIT with Compatibility level 130

SQL Server : add 'Type' to every column in table

I have a table DemoTable in SQL Server. And it has these columns:
Column1, Column2, Column3
I want to query the table
select * from DemoTable
but in query results I want to concatenate Type_ to all the column names available in DemoTable.
So the result of this query should be showing columns
Type_Column1, Type_Column2, Type_Column3
Is there any function or any way to achieve this?
Note: there are N number of columns not only 3 just to rename only these manually.
If the problem is as you say:
After joining all the tables , there are many duplicate column names
then the typical solution is to NOT use *. So instead of this:
SELECT *
FROM A
JOIN B ON ...
JOIN C ON ...
... you should consider using a custom column set, which is the normal and recommended way to do this, as in the following example:
SELECT A.Column1, A.Column2, B.Column3, C.Column4, C.Column5
FROM A
JOIN B ON ...
JOIN C ON ...
Here's one way to automate your task using dynamic SQL:
use MY_DATABASE;
go
--here you specify all your parameters, names should be self-explanatory
declare #sql varchar(1000) = 'select ',
#tableName varchar(100) = 'DemoTable',
#prefix varchar(10) = 'Type_';
select #sql = #sql + name + ' as ' + #prefix + name + ',' from sys.columns
where object_name(object_id) = #tableName;
set #sql = left(#sql, len(#sql) - 1) + ' from ' + #tableName;
exec(#sql);
Some general remarks:
Naming your result set's columns dynamically will demand for dynamic SQL in any case. No way around...
Naming columns to carry extra information is - in most cases - a very bad idea.
the only way I know to deal with the asterisk in a SELECT * FROM ... and still get full control over the columns names and types is XML.
Try this:
SELECT TOP 10 *
FROM sys.objects
FOR XML RAW, ROOT('TableDef'),ELEMENTS, XMLSCHEMA,TYPE
This will return the 10 first rows of sys.objects. The result is an XML, where the rows follow an XML schema definition.
It is possible (but sure not the best in performance) to create a fully inlined query dynamically. The result will be an EAV list carrying everything you need.
WITH PrepareForXml(QueryAsXml) AS
(
SELECT
(
SELECT TOP 10 *
FROM sys.objects
FOR XML RAW, ROOT('TableDef'),ELEMENTS, XMLSCHEMA,TYPE
)
)
,AllRows AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RowIndex
,rw.query('.') theRowXml
FROM PrepareForXml
CROSS APPLY QueryAsXml.nodes('TableDef/*:row') A(rw)
)
SELECT RowIndex
,B.ColumnName
,B.ColumnValue
,COALESCE(
(SELECT QueryAsXml.value('declare namespace xsd="http://www.w3.org/2001/XMLSchema";
(TableDef
/xsd:schema
/xsd:element
/xsd:complexType
/xsd:sequence
/xsd:element[#name=sql:column("ColumnName")]
/#type )[1]','nvarchar(max)')
FROM PrepareForXml)
,(SELECT QueryAsXml.value('declare namespace xsd="http://www.w3.org/2001/XMLSchema";
(TableDef
/xsd:schema
/xsd:element
/xsd:complexType
/xsd:sequence
/xsd:element[#name=sql:column("ColumnName")]
/xsd:simpleType
/xsd:restriction
/#base)[1]','nvarchar(max)')
FROM PrepareForXml)
) AS ColumnType
FROM AllRows
CROSS APPLY theRowXml.nodes('*:row/*') A(col)
CROSS APPLY (SELECT col.value('local-name(.)','nvarchar(max)') ColumnName
,col.value('(./text())[1]','nvarchar(max)') ColumnValue ) B;
This is the beginning of the result-set:
RowIndex ColumnName ColumnValue ColumnType
1 name sysrscols sqltypes:nvarchar
1 object_id 3 sqltypes:int
1 schema_id 4 sqltypes:int
[...many more...]
I don't know what you need actually, but it might be enough to export the XML as is. It's everything in there...
UPDATE: I did not read carefully enough...
You want to trick out the fact, that a result set's column names must be unique in order to continue with this...
The approach above will not solve this issue. Sorry.
I won't delete this immediately... Might be there are some hints you can get out of this...
You can use the following query to add 'Type' to every column in table:
SELECT Column1 AS Type_Column1, Column2 AS Type_Column2, Column3 AS Type_Column3
FROM DemoTable

Add character to selected duplicate value in SQL

I need to add character / number to selected duplicate values.
This is what I need:
SELECT Name -- Here I need to add for example 1 if It have duplicates
-- If It is hard way to code, how to add 1 to all selected values?
FROM Example
WHERE Id BETWEEN 25 AND 285
If there are 2 equal names Peter It should select Peter and second Peter1
If there is no easy way to make It, how to add 1 to all selected lines? Should select Peter1 instead of Peter
I've tried this:
SELECT Name + ' 1' AS Name -- in this case selecting wrong column
FROM Example
WHERE Id BETWEEN 25 AND 285
EDIT
SELECT #cols += ([Name]) + ','
FROM (SELECT Name --I neeed to integrate It here
FROM FormFields
WHERE ID BETWEEN 50 AND 82
) a
If I use this:
SELECT #cols += ([Name]) + ',' -- here throws error
FROM (SELECT Name + CASE WHEN RowNum = 1 THEN '' ELSE CONVERT(NVARCHAR(100), RowNum-1) END AS [UpdatedName]
FROM (
SELECT Name AS Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Name) AS "RowNum"
FROM FormFields
WHERE Id Between 50 And 82) x
) a
It throws error: Invalid column name 'Name'.
EDIT 2
It's different tests but some of them have the same criteria. That's why I need It to rename.
You can do this via getting the Row_Number and using a Case. Here's an example for SQL Server:
;With Cte As
(
Select Name, Row_Number() Over (Partition By Name Order By Name) RN
From Example
Where Id Between 25 And 285
)
Select Case When RN = 1 Then Name Else Name + Cast((RN - 1) As Varchar (3)) End As Name
From Cte
You could use the ROW_NUMBER function built into SQL server.
select Name + case when RowNum = 1 then '' else CONVERT(varchar(100), RowNum-1) end as "UpdatedName"
from (
select name as "Name",
ROW_NUMBER() over (partition by name order by name) as "RowNum"
from Example
Where Id Between 25 And 285) x
Please note that this still doesn't guarantee you unique names. Afterall, someone could already have a name of "MyName1", so if you had 2 people with names "MyName" you'd still get 2 "MyName1" with this select statement.
This is very unusual request, it looks like you are trying to "make car run with wheels on the roof" :)
The root problem is almost sure wrong database design... Pivot is usually used for data summaries. If you have in the same column "Peter" and "Peter" with different meanings, it looks that there is something wrong. Or do you need to differentiate both Peters for any other reason?
I do not understand what are you trying to achieve. If Peter is always Peter, and you just want to avoid duplicities, you can simply use "group by Name". But this is what pivot does automatically... If Peter and Peter have two different meanings (like Peter1 and Peter2), you should think about changing database structure, if possible.
Or try to explain more deeply what are you trying to achieve.
EDIT:
OK, now I understand the desired output. And what is the structure of your source data table(s)? From your schema it is clear that you need to make PIVOT columns based on
Testname+groupId
or
Testname+convert(varchar(100),groupId)
if groupId is number. That is your Peter1,Peter2 composition. It will create columns that you need. But I dont't know where testname and groupId are located in your datatables. Do test names correspond to column NAMES or to VALUES stored in DB? Is groupId something like TestId? Again column or value? Provide more info about source data structure, if you need more help, your problem is not so complicated.
Since the columns have group IDs, concatenate the Column name with an Underscore and GroupID as a key value and when you display it, strip the underscore and trailing characters.
Like This:
SELECT #cols += ([Name]) + ','
FROM (SELECT Name + '_' + CAST(GroupId AS varchar)
FROM FormFields
WHERE ID BETWEEN 50 AND 82
) a
I assume you are using this to build a dynamic SQL statement. I'm not sure what the schema of your FormFields Table is, but if it includes something like the test name you could append an AS [Name] + ' - ' +[TestName] to have the column header be something more useful. I would say try a PIVOT, but that could get pretty ungainly if the tests don't have the majority of the fields in common...
I also assume you are storing responses to these prompts in a table that looks something like this:
CREATE TABLE [Responses]
(
RespID int IDENTITY NOT NULL,
UserID int NOT NULL,
FieldID int NOT NULL,
RespVal int/varchar/whatever NOT NULL
)
Then perhaps you have a [Test] table with some test metadata that acts as the primary key for your GroupID Foreign key in your FormFields table.
In your example you show responses across all columns, but I'm not sure how that would work since (unless I'm missing something in your explanation and the inferences I've made to your design) one set of responses would only be populated for one of the groups per row, unless you are aggregating responses, but then by what criteria? Perhaps the rows correspond to respondents and all respondents are required to answer across all form types. In that case, your output would work as a PIVOT like this:
DECLARE #sql varchar(4000) = ''
DECLARE #colList varchar(1000)
DECLARE #selList varchar(1000)
;WITH NameBase
AS
(
SELECT t.Name [TestName], f.Name [FieldName], f.GroupId
FROM [FormFields] f
INNER JOIN [Tests] t ON f.GroupID = t.ID
)
SELECT #colList = COALESCE(#colList + ',','') + QUOTENAME([FieldName] + '_' + [GroupId])
, #selList = COALESCE(#selList + ',','') + QUOTENAME([FieldName] + '_' + [GroupId]) + ' AS ' + QUOTENAME([FieldName] + ' - ' + [TestName])
FROM NameBase
SELECT #sql = 'SELECT [UserName],' + #selList + ' FROM (
SELECT u.Name [UserName], f.Name + '_' + f.GroupId [FieldName], r.RespVal [Response]
FROM Responses r
INNER JOIN [TestUsers] u ON r.UserID = u.ID
INNER JOIN [FormFields] f ON r.FieldID = f.ID) t
PIVOT (MAX([Response]) FOR [FieldName] IN (' + #colList + ')) pvt'
EXECUTE(#sql);
I haven't tested that yet, but it should at least point you in the right direction. I'll try to build a SqlFiddle to test it in a little bit.

Create table from query

I managed to apply the PIVOT statement you suggested to transpose the values ​​of the records of a table as columns automatically:
DECLARE #PivotColumnas VARCHAR(MAX)
SELECT #PivotColumnas = COALESCE (#PivotColumnas + ',[' + IB_PDSBATCHATTRIBIDBI + ']', '[' + IB_PDSBATCHATTRIBIDBI + ']') FROM PDSBATCHATTRIB
DECLARE #PivotTablaSQL NVARCHAR(MAX)
SET #PivotTablaSQL = N' SELECT *
FROM (SELECT INVENTBATCHID, ITEMID, PDSBATCHATTRIB.IB_PDSBATCHATTRIBIDBI, PDSBATCHATTRIBVALUE FROM PDSBATCHATTRIBUTES
LEFT JOIN PDSBATCHATTRIB ON PDSBATCHATTRIBUTES.IB_PDSBATCHATTRIBIDBI = PDSBATCHATTRIB.IB_PDSBATCHATTRIBIDBI) AS TablaOrigen
PIVOT
(MIN(PDSBATCHATTRIBVALUE)
FOR IB_PDSBATCHATTRIBIDBI IN ('+ #PivotColumnas + ')) AS PivotTable'
EXECUTE (#PivotTablaSQL)
What I need is how to save the result as a query or create a table from this query. If I try to save the result as a query, I get the following error:
Incorrect syntax near the keyword 'DECLARE'.
Thanks!
Because it is a dynamic sql, and you don`t know the exact columns, you can use an SELECT ... INTO #TempPivot. It is creating a temporary table what you can use later, or try to build up a dynamic solution which can select the temp table's structure and create a table, however it seems a bit overkill.
I found the solution, is very simple in fact!
I only have to add the command INTO NewTable in the SELECT sentence oof the pivot table, just like that:
SET #PivotTablaSQL = N' SELECT * **INTO NewTable**
FROM (SELECT INVENTBATCHID, ITEMID, PDSBATCHATTRIB.IB_PDSBATCHATTRIBIDBI, PDSBATCHATTRIBVALUE FROM PDSBATCHATTRIBUTES
LEFT JOIN PDSBATCHATTRIB ON PDSBATCHATTRIBUTES.IB_PDSBATCHATTRIBIDBI = PDSBATCHATTRIB.IB_PDSBATCHATTRIBIDBI) AS TablaOrigen
PIVOT
(MIN(PDSBATCHATTRIBVALUE)
FOR IB_PDSBATCHATTRIBIDBI IN ('+ #PivotColumnas + ')) AS PivotTable'
This create a new table in the SQL database with the pivot table results.
Thanks all!

SQL query to find duplicate rows, in any table

I'm looking for a schema-independent query. That is, if I have a users table or a purchases table, the query should be equally capable of catching duplicate rows in either table without any modification (other than the from clause, of course).
I'm using T-SQL, but I'm guessing there should be a general solution.
I believe that this should work for you. Keep in mind that CHECKSUM() isn't 100% perfect - it's theoretically possible to get a false positive here (I think), but otherwise you can just change the table name and this should work:
;WITH cte AS (
SELECT
*,
CHECKSUM(*) AS chksum,
ROW_NUMBER() OVER(ORDER BY GETDATE()) AS row_num
FROM
My_Table
)
SELECT
*
FROM
CTE T1
INNER JOIN CTE T2 ON
T2.chksum = T1.chksum AND
T2.row_num <> T1.row_num
The ROW_NUMBER() is needed so that you have some way of distinguishing rows. It requires an ORDER BY and that can't be a constant, so GETDATE() was my workaround for that.
Simply change the table name in the CTE and it should work without spelling out the columns.
I'm still confused about what "detecting them might be" but I'll give it a shot.
Excluding them is easy
e.g.
SELECT DISTINCT * FROM USERS
However if you wanted to only include them and a duplicate is all the fields than you have to do
SELECT
[Each and every field]
FROM
USERS
GROUP BY
[Each and every field]
HAVING COUNT(*) > 1
You can't get away with just using (*) because you can't GROUP BY *
so this requirement from your comments is difficult
a schema-independent means I don't want to specify all of the columns
in the query
Unless that is you want to use dynamic SQL and read the columns from sys.columns or information_schema.columns
For example
DECLARE #colunns nvarchar(max)
SET #colunns = ''
SELECT #colunns = #colunns + '[' + COLUMN_NAME +'], '
FROM INFORMATION_SCHEMA.columns
WHERE table_name = 'USERS'
SET #colunns = left(#colunns,len(#colunns ) - 1)
DECLARE #SQL nvarchar(max)
SET #SQL = 'SELECT ' + #colunns
+ 'FROM USERS' + 'GROUP BY '
+ #colunns
+ ' Having Count(*) > 1'
exec sp_executesql #SQL
Please note you should read this The Curse and Blessings of Dynamic SQL if you haven't already
I have done this using CTEs in SQL Server.
Here is a sample on how to delete dupes but you should be able to adapt it easily to find dupes:
WITH CTE (COl1, Col2, DuplicateCount)
AS
(
SELECT COl1,Col2,
ROW_NUMBER() OVER(PARTITION BY COl1,Col2 ORDER BY Col1) AS DuplicateCount
FROM DuplicateRcordTable
)
DELETE
FROM CTE
WHERE DuplicateCount > 1
GO
Here is a link to an article where I got the SQL:
http://blog.sqlauthority.com/2009/06/23/sql-server-2005-2008-delete-duplicate-rows/
I recently was looking into the same issue and noticed this question.
I managed to solve it using a stored procedure with some dynamic SQL. This way you only need to specify the table name. And it will get all the other relevant data from sys tables.
/*
This SP returns all duplicate rows (1 line for each duplicate) for any given table.
to use the SP:
exec [database].[dbo].[sp_duplicates]
#table = '[database].[schema].[table]'
*/
create proc dbo.sp_duplicates #table nvarchar(50) as
declare #query nvarchar(max)
declare #groupby nvarchar(max)
set #groupby = stuff((select ',' + [name]
FROM sys.columns
WHERE object_id = OBJECT_ID(#table)
FOR xml path('')), 1, 1, '')
set #query = 'select *, count(*)
from '+#table+'
group by '+#groupby+'
having count(*) > 1'
exec (#query)