Is there a way to only select the columns whose names are not present on another table? For example
Table A Column B
ID | Name | Address Address
-----------------------
1 | Daniel | dummy
In this example my select statement should be like this:
select ID, Name from Column A
I've seen people talking about dynamic SQL but I can't find a decent example to solve my issue, any help is much appreciated.
Here is a version of the way you would do this using dynamic SQL:
declare #cols varchar(max);
set #cols = NULL;
select #cols = coalesce(#cols + ', ' + column_name, column_name)
from information_schema.columns ca
where ca.table_name = 'A' and
ca.column_name not in (select cb.column_name
from information_schema.columns cb
where cb.table_name = 'B'
);
declare #sql varchar(max);
set #sql = 'select [cols] from A';
set #sql = replace(#sql, '[cols]', #cols);
exec sp_executesql #sql;
This is a bit simplified to show how the information_schema tables can be sued. It will work in many circumstances, but is not maximally general:
It doesn't take schema name into account.
It assumes all names are simple ASCII.
It does not escape the column names (assuming the names do not need to be escaped).
Select the other table in the WHERE clause.
SELECT ID, NAME
FROM ColumnA
WHERE NAME NOT IN (SELECT NAME FROM COLUMNB)
Related
I would like to rename all column names of a table given a string with new names separated by comma. Here be the string:
declare #str varchar(max)='A,B,C'
The number of columns in a table may vary, and appropriately the number of names in a string. So in a 5-column table the string would be 'A,B,C,D,E'.
I have a table with column names as:
col1 | col2 | col3
and in expected results I would like to have:
A | B | C
Update
I have tried to follow that path:
SELECT
ORDINAL_POSITION
,COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'my_temp'
order by ORDINAL_POSITION asc
but I do not know how to split #sql string so that it can be applied to results in the following way:
ORDINAL_POSITION COLUMN_NAME sql_string
1 Col1 A
2 Col2 B
3 Col3 C
Then I could easily create a string like:
dynamic_sql='EXEC sp_rename my_table.' + COLUMN_NAME +',' + sql_string +', ''COLUMN'''
Using the splitter from Jeff Moden I have referenced in the comments here and the answer on your question you can do this easily. http://www.sqlservercentral.com/articles/Tally+Table/72993/
Here is a full working example. It would be really helpful if you could post sample tables in your questions so we don't have to do that to work on the questions.
if object_id('SomeTable') is not null
drop table SomeTable
create table SomeTable
(
Col1 int
, Col2 int
, Col3 int
)
--to demonstrate the column names before we change them
select * from SomeTable
declare #NewNames varchar(100) = 'A,B,C'
, #SQL nvarchar(max) = ''
select #SQL = #SQL + 'EXEC sp_rename ''SomeTable.' + c.name + ''', ''' + s.Item +''', ''COLUMN'';'
from sys.columns c
join dbo.DelimitedSplit8K(#NewNames, ',') s on s.ItemNumber = c.column_id
where object_id = object_id('SomeTable')
select #SQL
--exec sp_executesql #SQL
--demonstrates the column names have been changed.
select * from SomeTable
I have said this previously and I can't in good conscience not mention it again. The fact that you need to do this is a huge red flag that there is something very very wrong with the way you managing your data. Changing column names should not happen very often at all and only when absolutely required. Doing this routinely is a sign that there are really big issues with the process or the design.
This would give you the names of all the columns of an existing table in the correct order
select c.name
from sys.columns c
inner join sys.objects o on c.object_id = o.object_id
where o.name = 'name_of_table'
order by c.column_id
I have a scalar function that returns a query. The scalar function must return the query itself and not its' results, since it uses dynamic SQL and the variables are used as column names and table names.
So, I have something like this:
SELECT t.id,
dbo.queryToExecute(t.id, t.ColumnToFetch, t.TableToFetchFrom) QueryToExecute
FROM Table t
Which returns
| ID | QueryToExecute
| 1 | SELECT ColumnName1 FROM Table1 WHERE id = 1
| 2 | SELECT ColumnName2 FROM Table2 WHERE id = 2
While "QueryToExecute" returns a single value.
I want to do something like:
SELECT t.id,
EXEC(dbo.queryToExecute(t.id, t.ColumnToFetch, t.TableToFetchFrom)) ExecutedQuery
FROM Table t
So the result set will be:
| ID | ExecutedQuery
| 1 | Jacob
| 2 | Sarah
How can I do that?
I already have a stored procedure that gets the job done when I need to run individually, but I wanted to have the same thing on a scalar function in order to be able to embed the sub-results in more complex queries.
Thanks!
You need to use dynamic SQL:
DECALRE #sql nvarchar(max)
SELECT #sql = COALESCE(#sql,'') +
REPLACE(
dbo.queryToExecute(t.id, t.ColumnToFetch, t.TableToFetchFrom),
'SELECT ',
'SELECT '+ CAST(t.ID as nvarchar(max) +' as ID, '
) +' UNION ALL '
FROM Table t
SELECT #sql = LEFT(#sql,LEN(#sql)-LEN('UNION ALL '))
EXEC sp_executesql #sql
This will get all query's in one batch and execute it
You need to take your dynamic sql one step deeper, and include the id number.
This script will generate a select statement for each row, and union them all together. You then run this whole select in one go to get your output.
--set up a string variable to store your query
declare #tsql nvarchar(max);
set #tsql = '';
-- populate it with the string statements
select #tsql = #tsql + 'select ' + cast(t.id as nvarchar(10)) + ' as id , (' + dbo.queryToExecute(t.id, t.ColumnToFetch, t.TableToFetchFrom) + ') QueryToExecute union ' from Table t
--chop off the final "union"
select #tsql = left (#tsql,len(#tsql)-7);
--and run
EXEC sp_executesql #tsql
Assume I have a table with 3 columns. Is there possible sum of each column without specifying name of the column?
And is there possible create a table with dynamic name of the column, and then sum of each column?
UPDATE: Here is my sample.
First, I do a query and get the result like this:
---------
| Col |
---------
| DLX |
| SUI |
| PRE |
| TWQ |
---------
The number of row maybe different each time, and then I create a table with columns from rows above like this:
---------------------------------
| DLX | SUI | PRE | TWQ |
---------------------------------
And then I fill data the table from another table. After all, I sum each column. Because I will not know exactly name of the column, so I need sum of each column without specifying name of the column.
If your table is small (i.e. 10 columns) I would just do it manually. But if it's like 20+ columns, I would employ some dynamic sql.
To answer your question directly, yes, you can dynamically create a table with dynamic column names using dynamic sql.
Here's one way to do it:
You can use INFORMATION_SCHEMA.COLUMNS View to get all the column names and put them in a temp table.
SELECT NAME INTO #COLUMNS
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = YourTable
Next, create a temp table to store your sums.
CREATE TABLE #SUMS (
COLUMN_NAME NVARCHAR(MAX),
SUM_COLUMN INT
)
You can then use dynamic sql and a loop to sum each column.
WHILE EXISTS(SELECT TOP 1 * FROM #COLUMNS)
BEGIN
DECLARE #COLUMN NVARCHAR(MAX) = (SELECT TOP 1 * FROM #COLUMNS)
DECLARE #DYNAMICSQL NVARCHAR(MAX) = N'SELECT ' + #COLUMN + ' AS COLUMN_NAME, SUM(' + #COLUMN + ') FROM YourTable'
INSERT INTO #SUMS
EXEC SP_EXECUTESQL #DYNAMICSQL
DELETE FROM #COLUMNS
WHERE NAME = #COLUMN
END
Then you would need another dynamic sql and loop to loop through the new table and create a temp table with the column names you want using the sum values and the table name you want.
You should be able to do that using the code already supplied above.
At first I thought about pivoting but then came up with this:
DECLARE #Table TABLE ( --Table to generate input you need
[Col] nvarchar(3)
)
DECLARE #query nvarchar(max) -- a variable that will store dynamic SQL query
DECLARE #table_name nvarchar(max) = 'Temp' --A name of table to create
INSERT INTO #Table VALUES
('DLX'),
('SUI'),
('PRE'),
('TWQ')
SELECT #query = ISNULL(#query,'CREATE '+#table_name+' TABLE (') + QUOTENAME([Col]) + ' nvarchar(max),'
FROM #Table
SELECT #query = SUBSTRING(#query,1,LEN(#query)-1) +')'
EXEC sp_executesql #query
That will execute a query (PRINT #query to see the result below):
CREATE Temp TABLE ([DLX] nvarchar(max),[SUI] nvarchar(max),[PRE] nvarchar(max),[TWQ] nvarchar(max))
Which will create a temp table for you.
Then you can insert in that table in the pretty same way.
I'm working with MS SQL Server 2008. I'm trying to create a stored procedure to Merge (perhaps) several rows of data (answers) into a single row on target table(s). This uses a 'table_name' field and 'column_name' field from the answers table. The data looks like something like this:
answers table
--------------
id int
table_name varchar
column_name varchar
answer_value varchar
So, the target table (insert/update) would come from the 'table_name'. Each row from the anwsers would fill one column on the target table.
table_name_1 table
--------------
id int
column_name_1 varchar
column_name_2 varchar
column_name_3 varchar
etc...
Note, there can be many target tables (variable from answers table: table_name_1, table_name_2, table_name_3, etc.) that insert into many columns (column_name_1...2...3) on each target table.
I thought about using a WHILE statement to loop through the answers table. This could build a variable which would be the insert/update statement(s) for the target tables. Then executing those statements somehow. I also noticed Merge looks like it might help with this problem (select/update/insert), but my MS SQL Stored Procedure experience is very little. Could someone suggestion a strategy or solution to this problem?
Note 6/23/2014: I'm considering using a single Merge statement, but I'm not sure it is possible.
I'm probably missing something, but the basic idea to solve the problem is to use meta-programming, like a dynamic pivot.
In this particular case there is another layer to make the solution more difficult: the result need to be in different execution instead of beeing grouped.
The backbone for a possible solution is
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
--using a cursor on SELECT DISTINCT table_name FROM answers iterate:
--*Cursor Begin Here*
--mock variable for the first value of the cursor
DECLARE #table AS NVARCHAR(MAX) = 't1'
-- Column list
SELECT #cols = STUFF((SELECT distinct
',' + QUOTENAME(column_name)
FROM answers with (nolock)
WHERE table_name = #table
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
--Query definition
SET #query = '
SELECT ' + #cols + '
INTO ' + #table + '
FROM (SELECT column_name, answer_value
FROM answers
WHERE table_name = ''' + #table + ''') b
PIVOT (MAX(answer_value) FOR column_name IN (' + #cols + ' )) p '
--select #query
EXEC sp_executesql #query
--select to verify the execution
--SELECT * FROM t1
--*Cursor End Here*
SQLFiddle Demo
The cursor definition is omitted, because I'm not sure if it'll work on SQLFiddle
In addition to the template for a Dynamic Pivot the columns list is filtered by the new table name, and in the query definition there is a SELECT ... INTO instead of a SELECT.
This script does not account for table already in the database, if that's a possibility the query can be divided in two:
SET #query = '
SELECT TOP 0 ' + #cols + '
INTO ' + #table + '
FROM (SELECT column_name, answer_value
FROM answers
WHERE table_name = ''' + #table + ''') b
PIVOT (MAX(answer_value) FOR column_name IN (' + #cols + ' )) p '
to create the table without data, if needed, and
SET #query = '
INSERT INTO ' + #table + '(' + #cols + ')'
SELECT ' + #cols + '
FROM (SELECT column_name, answer_value
FROM answers
WHERE table_name = ''' + #table + ''') b
PIVOT (MAX(answer_value) FOR column_name IN (' + #cols + ' )) p '
or a MERGE to insert/update the values in the table.
Another possibility will be to DROP and recreate every table.
Approach I took to this complex problem:
Create several temporary tables to work with your data
Select and populate the temporary tables with the data
Use dynamic pivoting to pivot the rows into one row
Use a CURSOR with WHILE loop for multiple table entries
SET #query with the dynamically built MERGE statement
EXECUTE(#query)
Drop temporary tables
When we do SELECT * FROM table we get all records in a table, If I want to get only a row but do not know the number of columns
like
id att1 att2 att3.... attx
-------------------------------
1 45 5 3 7
How do I do a select statement that returns all columns?
I know I must use
from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTable'
and a Where clause: WHERE id = 1
but again I do not know the number of columns...
Just add a where clause to the end of the #sql statement to limit your selection to the rows you want:
declare #cols nvarchar(max)
select #cols = coalesce(#cols + ', ' + column_name, column_name)
from information_schema.COLUMNS
where TABLE_NAME = 'mytable'
declare #sql nvarchar(max)
select #sql = 'select ' + #cols + ' from myTable where Id = 1'
exec sp_executesql #sql
The * means that you want all columns, not all "records".
select *
from YourTable
where ID = 1
There is no need to know the number of columns if you are interesting in pulling out the details of all. Just give the where condition with select *.