SQL: Apply and union table function recursively - sql

I'm using SQL Server Management Studio 18.
I have a function that takes a table name as a parameter and outputs a table with info about other tables that have the same columns in it. Each table has a different amount of columns (that are also in other tables or not). The output is column names, table names and subject. This works.
I want to apply the same function to all tables that are in the result set of the first table I applied the function to, and union it with each other.
I know what I am doing wrong (dbo.TableStructure(firstTable.TableName)) doesn't work because the function is made for only 1 parameter and not multiple. But I don't know what to change to make it right.
The code of the function:
create function [dbo].[TableStructure](#table nvarchar(50))
returns table as return
(
select c.name as 'ColumnName', t.name as 'TableName', s.Subject as 'Subject'
from sys.columns c join sys.tables t on c.object_id = t.object_id join dbo.tableSubjects s on t.name=s.name
where t.name <> #table and c.name in (select name from sys.columns where object_id = (select object_id from sys.tables where name = #table)))
The code of applying the function:
declare #table varchar(50) = 'Example';
with firstTable as (select *, 1 as 'Counter' from dbo.TableStructure(#table));
union all
with tmpTable as (select *, 2 as 'Counter' from dbo.TableStructure(firstTable.TableName));

I think you just want cross apply:
with ts as (
select ts.*, 1 as Counter
from dbo.TableStructure(#table)
)
select ts.*
from ts
union all
select ts2.*, 2 as counter
from ts cross apply
dbo.TableStructure(ts.tablename) ts2;

Related

search for multiple columns in a single table sql

I am using SQL Server 2017. How can I search for multiple column names in a single table?
I can search for a single column name (USERID) in my database with:
select *
from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME like '%USERID%'
order by TABLE_NAME
But, I want to search for all tables that have both USERID and DOB.
Using:
select *
from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME like '%USERID%' OR COLUMN_NAME like '%DOB%'
order by TABLE_NAME
returns a list of all tables that contain the column USERID as well as all tables that contain DOB.
Changing to AND results in a completely empty list, which is correct, as a single column would not contain both terms
You can try below -
select TABLE_NAME from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME like '%USERID%' OR COLUMN_NAME like '%DOB%'
group by TABLE_NAME
having count(distinct COLUMN_NAME)=2
Below query will give you desired results:
;WITH cte
AS (SELECT object_id , ROW_NUMBER() OVER(PARTITION BY object_id
ORDER BY object_id) AS rn
FROM sys.columns AS c
WHERE c.name = 'USERID'
OR
c.name = 'DOB')
SELECT s.name + '.' + t.name
FROM sys.tables AS t INNER JOIN cte AS c ON t.object_id = c.object_id
INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
WHERE c.rn > 1;
Please note to use exact column names with = operator and not like operator to get the correct results.

SQL select resultOfSelect from table

I’m a beginner to SQL and am struggling with the following:
I have two tables with the same columns except one column from table A is not a column of table B and table B has a column that table A doesn’t have. I want to copy data from
one into the other table.
I got the following SQL, that gives me the column-names of my table B:
SELECT name
FROM sys.columns
WHERE object_id = OBJECT_ID(’TABLEB’)
AND name <> COLUMNNOTINA
I want to use that select as a query:
SELECT
(SELECT name
FROM sys.columns
WHERE object_id = OBJECT_ID(’TABLEB’)
AND name <> COLUMNNOTINA), 0
FROM
TABLEA
This does not work, but how do I make this work?
Thank you in advance!
Let's say there is some common thing connecting the two tables
For example, ORDINAL_POSITION
T1.ORDINAL_POSITION = T2.ORDINAL_POSITION
Is there a description or property to compare each column?
select * from (
(SELECT name FROM sys.columns WHERE object_id = OBJECT_ID(’TABLEA’) ) as T1
Inner join
(SELECT name FROM sys.columns WHERE object_id = OBJECT_ID(’TABLEB’) ) as T2
ON T1.ORDINAL_POSITION = T2.ORDINAL_POSITION
)
Where T1.ColName <> T2.ColName

SQL Server Join - With INFO_SCHEMA information

I have the first table:
select COLUMN_NAME
from Emerald_Data.INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = N'tbl_Client_List_Pricing'
Don't mind the numbering in the Column_Name. I was doing this while testing because I need the order to remain as they are in the table. Not by ASC, DESC.
Anyhow, I don't know how to use the row numbers on the left that the system provides to JOIN another table without a condition.
Here is Table 2:
You can see that the left row numbers are my linking value but I don't know how to use that system index value as a condition in my JOIN.
Or if there is another way to join these two tables without a condition while keeping the Table 1 information in it's correct position and not affecting it by ORDER would be much appreciated.
Thank you!
-Chase
I guess you are looking for row_number. Use row_number to order result of two queries then join by matching order nums. Your query would be something like
with query_1 as (
select COLUMN_NAME
, rn = row_number() over (order by cast(left(COLUMN_NAME, 3) as int))
from Emerald_Data.INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = N'tbl_Client_List_Pricing'
)
, query_2 as (
select
*, rn = row_number() over (order by (select null))
from
Table_2
)
select
*
from
query_1 q1
join query_2 q2 on q1.rn = q2.rn
select COLUMN_NAME from Emerald_Data.INFORMATION_SCHEMA.COLUMNS
inner join with Table_2 on Num=cast(LEFT(COLUMN_NAME,CHARINDEX('-', COLUMN_NAME)) AS int)
where TABLE_NAME = N'tbl_Client_List_Pricing'
You could also use sys.all_columns object which could able to state the index for your desired column & JOIN them with table2
SELECT *
FROM sys.all_columns c
INNER JOIN Table2 t ON t.Num = c.column_id
WHERE OBJECT_NAME(object_id) = 'tbl_Client_List_Pricing'

Finding all tables with matching columns

I have 81 tables where I want to find matching columns and output a list like:
"columnName" found in 3 tables:
table1
table2
table3
"columnName2" found in 4 tables:
table1
table3
table4
table5
The table INFORMATION_SCHEMA.COLUMNS is a system table which contains all columns for all tables. You can look at it by selecting from it.
For your question, you should try this :
select I.Column_name,
table_name,
table_count
from INFORMATION_SCHEMA.COLUMNS I
inner join
(
Select Column_name,
count(*) as table_count
from INFORMATION_SCHEMA.COLUMNS
group by Column_name) as T on T.Column_name = I.Column_name
You will have a table with, for each column_name, the tables, and the count in the column table_count.
I ust don't know how to output a list. You should put it on excel then if you want to format it I guess ..
Tell me if you have problem !
Below Query should give you the desired result. Let me know if you face any issues. You can modify script if you want to be specific about the single database
This query provides you Column Name - Concatinated Table Names - Number of Tables
SELECT t.CNAME AS ColumnName, STUFF(
(SELECT ',' + S.TNAME
FROM
(
SELECT C.NAME AS CNAME , T.NAME AS TNAME
FROM SYS.OBJECTS AS T
JOIN SYS.COLUMNS AS C
ON T.OBJECT_ID=C.OBJECT_ID
WHERE T.TYPE_DESC='USER_TABLE'
)s
WHERE s.CNAME = t.CNAME
FOR XML PATH('')),1,1,'') AS TablesUsed,
COUNT(t.TNAME)
FROM
(
SELECT C.NAME AS CNAME , T.NAME AS TNAME
FROM SYS.OBJECTS AS T
JOIN SYS.COLUMNS AS C
ON T.OBJECT_ID=C.OBJECT_ID
WHERE T.TYPE_DESC='USER_TABLE'
)t
GROUP BY t.CNAME
HAVING COUNT(t.TNAME) > 1
ORDER BY COUNT(t.TNAME) DESC

Joining a table onto itself in SQL and saving the result

In SQL, I am joining a table onto itself:
SELECT * FROM table AS a
LEFT JOIN table AS b
ON a.NAME = b.NAME
So it's fetching all the rows which have the same NAME appearing in a row of the other table (will also return the same row twice!).
Let's say that I want to save the result into a temporary table, say something like:
SELECT * INTO ##temp_table FROM (
SELECT * FROM table AS a
LEFT JOIN table AS b
ON a.NAME = b.NAME
)
Oh dear. SQL says:
The column 'NAME' was specified multiple times.
Reason: SQL can only create a table if every column name is unique.
Obvious solution: give every column name in the "second table" an alias.
My problem is that the actual tables I'm working with have about 40 columns. Giving every one of those columns an alias seems like a wasteful use of time. Sure, I don't need every column so could drop some of them, but deciding which ones I require just now also seems wasteful.
Question: is there a shorthand way to rename every column? For example, can I append every column name with a _2 or an _a?
Ok, you have a query, with 2 joined tables, wich returns both tables columns (i don't care if you are joining the same table with itself).
So you have two possible results
Show both colums, with differents alias (AS)
SELECT * INTO ##temp_table FROM (
SELECT a.Name AS NameA, b.Name AS NameB FROM table AS a
LEFT JOIN table AS b
ON a.NAME = b.NAME
)
Or, if you don't want them duplicated (because the other will return two times the same name)
SELECT * INTO ##temp_table FROM (
SELECT a.Name FROM table AS a
LEFT JOIN table AS b
ON a.NAME = b.NAME
)
And what if you have more colums? Ok, you can just show one of the tables in the JOIN
SELECT * INTO ##temp_table FROM (
SELECT b.* FROM table AS a
LEFT JOIN table AS b
ON a.NAME = b.NAME
)
Sorry for my bad english! I hope this can help you!
I suggest querying sys.tables and sys.columns to get your renamed fields quickly:
SELECT c.name + '_2,' ColumnName
FROM sys.columns c
JOIN sys.tables t
ON c.object_id = t.object_id
WHERE t.name = 'YourTable'
Or you can combine with the OBJECT_ID() function:
SELECT c.name + '_2,' ColumnName
FROM sys.columns c
WHERE object_id = OBJECT_ID('YourTable')