Rename columns in 1 table to row values in another table - sql

Ok, so I have 2 tables in all:
Table 1 has these 3 columns which are not meaningful as they are just a varchar value:
Q19243 Q19244 Q19245
Table 2 has 2 columns ColumnName and TextValue.
ColumnName holds the values of the name of the 3 columns in Table 1 (Q19243 etc) and also has a corresponding column called TextValue which holds a friendly description of what Q19243 actually means.
So there are 3 records in Table 2, 1 for each column in Table 1.
I would like to rename these 3 columns in Table 1 to equal whatever is in the TextValue column in Table 2. I would like to do this dynamically rather than a simple UPDATE statement to rename the columns. Sorry I did not attach screen shots but I do not see an attach button to do so...
If you run this code to create an example of the 2 tables then you should probably have a better idea of what I'm referring to.
create table #Table1 (Q19243 varchar(10),Q19244 varchar(10),Q19245 varchar(10))
Create table #Table2 (ColumnName varchar(10),TextValue varchar(50))
Insert into #Table2 select 'Q19243','Provider Name'
Insert into #Table2 select 'Q19244','The Provider You Usually See'
Insert into #Table2 select 'Q19245','How Long Going to Provider'
select * from #Table1
select * from #Table2
drop table #Table1
drop table #Table2

Since the purpose of the column rename is for output purposes only, you can use a query against Table2 to create Dynamic SQL specific to Table1 that aliases the column names on the SELECT.
(the following example uses the sample code in the original question and only differs by what is between the --============== lines)
create table #Table1 (Q19243 varchar(10),Q19244 varchar(10),Q19245 varchar(10))
Create table #Table2 (ColumnName nvarchar(10),TextValue nvarchar(50))
Insert into #Table2 select 'Q19243','Provider Name'
Insert into #Table2 select 'Q19244','The Provider You Usually See'
Insert into #Table2 select 'Q19245','How Long Going to Provider'
select * from #Table1
select * from #Table2
--=========================================
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = COALESCE(#SQL + N',', N'SELECT')
+ N' t1.'
+ t2.ColumnName
+ N' AS ['
+ t2.TextValue
+ N']'
FROM #Table2 t2
SET #SQL = #SQL + N' FROM #Table1 t1'
SELECT #SQL
EXEC(#SQL)
--=========================================
drop table #Table1
drop table #Table2
The value of #SQL after the SELECT #SQL= query is:
SELECT t1.Q19243 AS [Provider Name], t1.Q19244 AS [The Provider You
Usually See], t1.Q19245 AS [How Long Going to Provider] FROM #Table1
t1
Note: you need the square-brackets around the field name alias (value from Table2.TextValue) as there are spaces in the string.

Related

How to add multiple columns from another table in sql server 2017?

I have a requirement where i need to add multiple columns from a source table after checking existence of those columns. for eg:
Table1 containg 7 coulmns like A, B, C, D, E, F, G and Table2 containing 4 columns like A, B, C, D
I want to check the existency of table1 columns in Table2 and if not exists then add rest 3 columns in Table2. I am looking for a solution where i don't need to add these columns manually if not exists in table2.
How can i do this?
I have tried this:
if exists (SELECT TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='table1' and COLUMN_NAME in('A','B','C','D','E','F','G'))
BEGIN
ALTER TABLE table2
ADD [E] FLOAT null
,[F] FLOAT null
,[G] FLOAT null
END;
But this is not the solution of my query I want to make it dynamic and don't know how to do this.
I don't, for one second, think this is a good idea, but this would achieve what you are after. Note that if the same column exists by name in both tables, but have different data types, the column will be ignored:
CREATE TABLE Table1 (a int,
b numeric(12,2),
c datetime2(0),
d date,
e varchar(20),
f sysname,
g varbinary);
CREATE TABLE Table2 (a int,
b numeric(12,2),
c datetime2(0),
d date);
GO
DECLARE #SQL nvarchar(MAX);
SET #SQL = STUFF((SELECT NCHAR(13) + NCHAR(10) +
N'ALTER TABLE Table2 ADD ' + QUOTENAME(T1.name) + N' ' + T1.system_type_name + N';'
FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM Table1',NULL, NULL) T1
WHERE NOT EXISTS(SELECT 1
FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM Table2',NULL, NULL) T2
WHERE T1.[name] = T2.[name])
ORDER BY T1.column_ordinal
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,2,N'');
PRINT #SQL;
EXEC sp_executesql #SQL;
GO
SELECT *
FROM dbo.Table2;
GO
DROP TABLE dbo.Table2;
DROP TABLE dbo.Table1;

how to get a select count(x) from a query of table names

I have a query the brings back a list of tables and the counts of those tables.
select *
from error
with a result of
tablename | errorcnt
----------+---------
table1 | 5
table2 | 256
and so on.
I need to do a join so I can get another count from each table as to the records that have been corrected example
select count(fixed)
from table1
so my new result would be
tablename | errorcnt | fixed
----------+----------+------
table1 | 5 | 3
table2 | 256 | 239
and so on.
Without doing a cursor how could I do? I guess a sub query using 'tablename'.
The comment you made:
This is how i populate my errortable SELECT T.name TableName,i.Rows
NumberOfRows FROM sys.tables T JOIN sys.sysindexes I ON T.OBJECT_ID =
I.ID WHERE indid IN (0,1) ORDER BY i.Rows DESC,T.name
Means you are looking for tables, and their respective indexes, for tables that are either a heap (i.e. has no index) or have a clustered index. I'm not sure why this would classify as an "error". I'd expect you to want to look for only heaps. i.e. on where indid = 0. Regardless, I suppose the "fixed" would be to return tables that, for example, didn't have a clustered index which now does. In that case I don't understand the schema and think you have asked a XY Question
With that being said,based off the other comments, you could use derived tables and join on the literal values of error.tablename to prevent the use of a cursor.
select
error.tablename
,error.errorcnt
,fixed = coalesce(t1.ct, t2.ct) --add in for each join.
from
error
left join (select count(fixed) as ct from table1 where fixed = 'Y') as t1 on error.tablename = 'table1'
left join (select count(fixed) as ct from table2 where fixed = 'Y') as t2 on error.tablename = 'table2'
--continue the joins for all values in error.tablename
A cursor would be less code, and dynamic, but you asked for a way without a cursor.
you can use temp table and while loop avoid cursor
DECLARE
#SQLQuery NVARCHAR(100),
#Tablename VARCHAR(100)
CREATE TABLE
#error
(
tablename VARCHAR(100),
errorcnt INT
)
CREATE TABLE
#Table1
(
fixed INT
)
CREATE TABLE
#Table2
(
fixed INT
)
CREATE TABLE
#Temp_fixed
(
fixed INT
)
INSERT INTO
#error
VALUES
(
'#Table1',
5
),
(
'#Table2',
256
)
INSERT INTO
#Table1
VALUES
(
3
)
INSERT INTO
#Table2
VALUES
(
239
)
SELECT
tablename,
errorcnt,
-1 AS fixed
INTO
#Temp_error
FROM
#error
WHILE EXISTS(SELECT TOP 1 1 FROM #Temp_error WHERE fixed = -1)
BEGIN
SET
#Tablename = (SELECT TOP 1 tablename FROM #Temp_error WHERE fixed = -1)
SET
-- #SQLQuery = 'SELECT COUNT(fixed) FROM ' + #Tablename
#SQLQuery = 'SELECT SUM(fixed) FROM ' + #Tablename
INSERT INTO
#Temp_fixed
(
fixed
)
EXECUTE
sp_executesql
#SQLQuery
UPDATE
#Temp_error
SET
fixed = ISNULL((SELECT TOP 1 fixed FROM #Temp_fixed), 0)
WHERE
tablename = #Tablename
TRUNCATE TABLE #Temp_fixed
END
SELECT
tablename,
errorcnt,
fixed
FROM
#Temp_error
DROP TABLE #error
DROP TABLE #Table1
DROP TABLE #Table2
DROP TABLE #Temp_error
DROP TABLE #Temp_fixed

SQL copying record with out specifying column list; ignoring Identity

I'm trying to copy a record from TableA back to TableA, but using a new Identity.
I don't want to specify column list as I have over 100 columns, and there may be more in the future. Id like a chunk of code that can run when/if things change.
After looking similar questions, I have attempted this code
SELECT * INTO #tmp FROM TableA WHERE Id = 1;
ALTER TABLE #tmp DROP COLUMN Id;
INSERT INTO TableA SELECT * FROM #tmp;
Drop Table #tmp;
I am however still getting this error
An explicit value for the identity column in table 'dbo.TableA' can only be specified when a column list is used and IDENTITY_INSERT is ON.
Running a Select * FROM #tmp gives me what I would expect. A single record with all my Columns with the exception of the Id column.
Any Ideas?
Thanks!
EDIT
Here is a pictures of the properties of the Id Column
Use Dynamic SQL: get your list of columns (except ID), build an insert statement using that list, and then call exec on it:
SELECT *
INTO #tmp
FROM TableA
WHERE Id = 1;
ALTER TABLE #tmp DROP COLUMN id;
DECLARE #cols varchar(max);
SELECT
#cols = COALESCE(#cols + ',', '') + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TableA' AND COLUMN_NAME <> 'id'
--select #cols -- display column list for debugging
DECLARE #sqltxt varchar(max) = 'INSERT INTO TableA (' + #cols + ') SELECT * FROM #tmp';
--SELECT #sqltxt -- display the statement for debugging
exec (#sqltxt)
DROP TABLE #tmp
Try This
Step 1 :
INSERT INTO Employee1(FirstName,LastName,ManagerID,Salary)
SELECT FirstName,LastName,ManagerID,Salary
FROM Employee1
WHERE EmployeeID=X -- Your Emplyee ID
Step 2:
DELETE FROM Employee1 WHERE EmployeeID=X

Update a column in a table repeatedly

I have a table like this
CREATE TABLE #tmp(ColSelect NVARCHAR(400),ColParValues XML)
that ColSelect contains SQL Select Statement and ColParValues contains some xml data for parameter value in ColSelect
for example ColSelectcontains:
"SELECT [$12]+19/[$16]-[$54]"
and col 2 contains name value pair that refer to ColSelect parameters
How can I update my table that replace each parameter with relevant value from ColParValues. I use this statement:
update #tmp
SET
ColSelect=REPLACE(ColSelect,c.value('#Value','nvarchar(10)'),c.value('#Res','DECIMAL(24,12)'))
FROM #tmp t1
CROSS APPLY t1.ColParValues.nodes('/root/r') AS n(c)
but this statement replace just one parameter value in each row.
And this is sample data link
This is one solution :
create table #tmp (colselect varchar(200),colparvalues xml)
insert into #tmp (colselect,colparvalues)
values ('select case when [$71]+[$29]+10<25 then 1 else 0 end'
,'<root><r Value="[$71]" Res="1"/><r Value="[$29]" Res="5"/></root>'),
('select case when [$95]*[$29]+10<25 then 1 else 0 end'
,'<root><r Value="[$95]" Res="3"/><r Value="[$29]" Res="5"/></root>')
WHILE ##ROWCOUNT >0
update #tmp
SET
ColSelect=REPLACE(ColSelect,c.value('#Value','nvarchar(10)'),c.value('#Res','DECIMAL(24,12)'))
FROM #tmp t1
CROSS APPLY t1.ColParValues.nodes('/root/r') AS n(c)
WHERE t1.colselect LIKE'%'+replace(c.value('#Value','nvarchar(10)'),'[','')+'%'
select * from #tmp
drop table #tmp
However, very much similar to cursor in performance. Check the performance. Use if ok.

Search if a string word exists between two different tables in a comma-delimited field

I have two tables:
EmployeeTypeA table
Name varchar(2000) field contains - 'john,sam,doug'
EmployeeTypeB table
Name varchar(2000) field contains - 'eric,sam,allen,stephanie'
What is the most efficient way to return a true or false when a name is found in both lists using MS SQL? This needs to be done within a stored procedure so I cannot use a programming language to manipulate it outside of SQL. In this example since 'sam' is in both tables I would want to return a true (or 0,etc)
Should I separate the comma-delimited field first and then put the items in a temp table? Or use cursors?
Thanks
Separate the comma-delimited field first into a temporary table or table variable. That way, you can join or match rows accurately. Make a row for each name and include a key column of some sort that will help you correlate the rows.
The best way to do this is with a "helper table" like so:
DECLARE #numbers TABLE (number int)
DECLARE #i int
SET #i = 1
WHILE (#i < 1001)
BEGIN
INSERT INTO #numbers (number) VALUES (#i)
SET #i = #i+1
END
DECLARE #TestString VARCHAR(200)
SET #TestString = 'andy,john,mark'
DECLARE #RowDelimiter VARCHAR(1)
SET #RowDelimiter=','
SELECT SUBSTRING(#TestString+#RowDelimiter, number,
CHARINDEX(#RowDelimiter, #TestString+#RowDelimiter, number) - number)
FROM #numbers
WHERE number <= LEN(#TestString)
AND SUBSTRING(#RowDelimiter+ #TestString, number, 1) = #RowDelimiter
ORDER BY number
-- helper table technique: bill#creaticle.com
the result is:
andy
john
mark
Once you have the two temporary tables, then do a FULL OUTER JOIN and include your "found in both" column with a set value. You'll get the NULL value for names not found in both - and you can treat the NULL as the "False" value.
Can you mention why you need to get a boolean value for matches between the two tables? What are you going to do with it next? Sometimes explaining that will lead to better solutions. You might find that you are making assumptions hastily. Best, Bill.
Untested:
SELECT COUNT(*) FROM EmployeeTypeA
WHERE ',' + nameListField + ',' LIKE '%,' + #searchedName + ',%'
should return some value > 0 if the name has been found in one of the lists of the first table. Do the same for the second table and return true if both SELECTs returned a non-zero value.
PS: If you have the authority to change the database design: Do it. A normalized database should not contain comma-separated lists but rather subtables with a foreign key relationship.
Here is a script that creates the two test tables and returns a list of the names with 'True' if the name is in both tables. It works by using a left join to find the names that are in both tables or only in table A. This result set is unioned to a right join to get the names that are only in table B.
DROP TABLE EmployeeTypeA
DROP TABLE EmployeeTypeB
GO
CREATE TABLE EmployeeTypeA
(Name VARCHAR(2000))
GO
CREATE TABLE EmployeeTypeB
(Name VARCHAR(2000))
GO
INSERT INTO EmployeeTypeA VALUES ('john')
INSERT INTO EmployeeTypeA VALUES ('sam')
INSERT INTO EmployeeTypeA VALUES ('doug')
INSERT INTO EmployeeTypeB VALUES ('eric')
INSERT INTO EmployeeTypeB VALUES ('sam')
INSERT INTO EmployeeTypeB VALUES ('allen')
INSERT INTO EmployeeTypeB VALUES ('stephanie')
GO
SELECT
eta.Name,
CASE
WHEN etb.Name IS NULL THEN 'False'
ELSE 'True'
END
FROM
EmployeeTypeA eta
LEFT JOIN EmployeeTypeB etb ON
eta.Name = etb.Name
UNION
SELECT
etb.Name,
'False'
FROM
EmployeeTypeA eta
RIGHT JOIN EmployeeTypeB etb ON
eta.Name = etb.Name
WHERE
eta.Name IS NULL
GO
This is part 2 from me above so I can add additional code. This part explains how to get your boolean value of whether or not there is a match between the tables after you've ripped your names into separate rows.
DECLARE #LeftTable TABLE (thisid int, thisname varchar(50))
INSERT INTO #LeftTable VALUES (1, 'andy')
INSERT INTO #LeftTable VALUES (2, 'bill')
INSERT INTO #LeftTable VALUES (3, 'zed')
DECLARE #RightTable TABLE (thisid int, thisname varchar(50))
INSERT INTO #RightTable VALUES (1, 'chris')
INSERT INTO #RightTable VALUES (2, 'bill')
INSERT INTO #RightTable VALUES (3, 'zed')
SELECT
a.thisname AS theleftname,
b.thisname AS therightname,
CASE
WHEN (ISNULL(a.thisname,'') = '' OR ISNULL(b.thisname,'') = '') THEN 'False'
ELSE 'True'
END
AS namematches
FROM #LeftTable a
FULL OUTER JOIN #RightTable b
ON a.thisname = b.thisname
-- www.caliberwebgroup.com
Here are the results:
theleftname therightname namematches
NULL chris False
bill bill True
zed zed True
andy NULL False
You can write a table valued function which takes in a comma separated string and returns a table (one column) of string.
Create FUNCTION [dbo].[SplitStrings]
(
#StringList varchar(max)
)
RETURNS
#Outputable table
(
ParsedItem varchar(2000)
)
as
-- you can have a while loop here to populate the table.
This will also make your code resuable.
But remember this might a performance bottleneck if you use for a lot of rows..It runs for every row.
UPdated!!!
Yes offcourse you can use the join from other answer once you get the tables.
Try this
declare #EmployeeTypeA table(Name VARCHAR(2000))
insert into #EmployeeTypeA select 'john,sam,doug'
declare #EmployeeTypeB table(Name VARCHAR(2000))
insert into #EmployeeTypeB select 'eric,sam,allen,stephanie'
--Program starts
declare #xA xml
declare #xB xml
select #xA = '<i>' + REPLACE(Name, ',', '</i><i>') + '</i>' from #EmployeeTypeA
select #xB = '<i>' + REPLACE(Name, ',', '</i><i>') + '</i>' from #EmployeeTypeB
select
EmployeeTypeA
,EmployeeTypeB
from (
SELECT
EmployeeTypeA
,i.value('.', 'VARCHAR(MAX)') EmployeeTypeB
FROM #xB.nodes('//i') x(i)
cross apply(
SELECT i.value('.', 'VARCHAR(MAX)') EmployeeTypeA
FROM #xA.nodes('//i') x(i)) Y) Res(EmployeeTypeA,EmployeeTypeB)
where EmployeeTypeA = EmployeeTypeB
Output:
EmployeeTypeA EmployeeTypeB
sam sam