Selecting column names separated by a comma - sql

I want to write a SQL statement (SQL Server) which selects column names under specific conditions and "returns" all column names separated by a ,.
SELECT COLUMN_NAME
FROM all_tab_columns
WHERE OWNER = 'KOCH' AND TABLE_NAME = 'TABLE1';
This returns several rows of column names:
NAME
ID
STATE
CITY
But I want them to be returned in this format:
NAME, ID, STATE, CITY
(I think I have to use FROM dual)?

For Oracle:
;WITH CTE_Columns (column_list, column_id) AS
(
SELECT
CAST(COLUMN_NAME AS VARCHAR(500)) AS column_list,
COLUMN_ID
FROM USER_TAB_COLUMNS C
WHERE
TABLE_NAME = 'TABLE1' AND
COLUMN_ID= 1
UNION ALL
SELECT
CAST(column_list + ', ' + C.COLUMN_NAME AS VARCHAR(500)),
C.COLUMN_ID
FROM
CTE_Columns CL
INNER JOIN USER_TAB_COLUMNS C ON
C.TABLE_NAME = 'TABLE1' AND
C.COLUMN_ID = CL.COLUMN_ID + 1
)
SELECT column_list
FROM CTE_Columns
WHERE
COLUMN_ID =
(
SELECT MAX(COLUMN_ID)
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'TABLE1')

Related

Retrieve tables based on its contents in SQL server

I'd like to retrieve all tables and the associated column values where two of their specific columns (the column names will be passed into) that don't have the exact same content in them.
Here's a more definite break-down of the problem. Suppose, the columns that I need to look into is 'Column_1' and 'Column_2'
First identify from in INFORMATION_SCHEMA which of the tables have both of these columns present in them(possible one sub-query),
And then identify which of these tables don't have exact same content on these 2 columns meaning Column_1 != Column_2.
The following section would retrieve all the tables that has both 'Column_1' and 'Column_2'.
SELECT
TABLE_NAME
FROM
INFORMATION_SCHEMA.TABLES T
WHERE
T.TABLE_CATALOG = 'myDB' AND
T.TABLE_TYPE = 'BASE TABLE'
AND EXISTS (
SELECT T.TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE
C.TABLE_CATALOG = T.TABLE_CATALOG AND
C.TABLE_SCHEMA = T.TABLE_SCHEMA AND
C.TABLE_NAME = T.TABLE_NAME AND
C.COLUMN_NAME = 'Column_1')
AND EXISTS
(
SELECT T.TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE
C.TABLE_CATALOG = T.TABLE_CATALOG AND
C.TABLE_SCHEMA = T.TABLE_SCHEMA AND
C.TABLE_NAME = T.TABLE_NAME AND
C.COLUMN_NAME = 'Column_2')
As the next step, I tried to use this as a sub-query and have the following at the end but that doesn't work and sql-server returns 'Cannot call methods on sysname'. What would the next step on this? This problem assumes all columns has the exact same Data-type.
WHERE SUBQUERY.TABLE_NAME.Column_1 != SUBQUERY.TABLE_NAME.Column_2
This is what's expected :
Table_Name
Column_Name1
Column_Value_1
Column_Name2
Column_Value_2
Table_A
Column_1
abcd
Column_2
abcde
Table_A
Column_1
qwerty
Column_2
qwert
Table_A
Column_1
abcde
Column_2
eabcde
Table_B
Column_1
zxcv
Column_2
zxcde
Table_C
Column_1
asdfgh
Column_2
asdfghy
Table_C
Column_1
aaaa
Column_2
bbbb
If in fact you want to actually compare values (not length) between two columns in tables that contain those two columns, you will need to generate dynamic SQL and then execute it. This could be done semi-automatically with the following:
DECLARE #SqlTemplate VARCHAR(MAX) =
'UNION ALL'
+ ' SELECT Table_Name = <TNAME>'
+ ', Column_Name1 = <C1NAME>, Column_Value_1 = <C1>'
+ ', Column_Name2 = <C2NAME>, Column_Value_2 = <C2>'
+ ' FROM <T>'
+ ' WHERE ISNULL(<C1>, '(null)') <> ISNULL(<C2>, '(null)')'
SELECT T.TABLE_NAME
, REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
#SqlTemplate
, '<TNAME>', QUOTENAME(T.TABLE_SCHEMA + '.' + T.TABLE_NAME, ''''))
, '<C1NAME>', QUOTENAME(C1.COLUMN_NAME, ''''))
, '<C2NAME>', QUOTENAME(C2.COLUMN_NAME, ''''))
, '<T>', QUOTENAME(T.TABLE_SCHEMA) + '.' + QUOTENAME(T.TABLE_NAME))
, '<C1>', QUOTENAME(C1.COLUMN_NAME))
, '<C2>', QUOTENAME(C2.COLUMN_NAME))
FROM INFORMATION_SCHEMA.TABLES T
JOIN INFORMATION_SCHEMA.COLUMNS C1
ON C1.TABLE_CATALOG = T.TABLE_CATALOG
AND C1.TABLE_SCHEMA = T.TABLE_SCHEMA
AND C1.TABLE_NAME = T.TABLE_NAME
AND C1.COLUMN_NAME = 'Column_1'
JOIN INFORMATION_SCHEMA.COLUMNS C2
ON C2.TABLE_CATALOG = T.TABLE_CATALOG
AND C2.TABLE_SCHEMA = T.TABLE_SCHEMA
AND C2.TABLE_NAME = T.TABLE_NAME
AND C2.COLUMN_NAME = 'Column_2'
WHERE T.TABLE_CATALOG = 'myDB'
AND T.TABLE_TYPE = 'BASE TABLE'
This would generate sql for each qualifying table of the form:
UNION ALL SELECT Table_Name = 'dbo.Z', Column_Name1 = 'X', Column_Value_1 = [X], Column_Name2 = 'Y', Column_Value_2 = [Y] FROM [dbo].[Z] WHERE ISNULL([X], '(null)') <> ISNULL([Y], '(null)')
After running the above, you would then cut & paste the generated SQL into another query window, remove the initial 'UNION ALL', and then execute the remaining SQL to get the final results.
There are ways of combining all the SQL into a single string and executing it automatically, but your problem sounds like a one-off process that doesn't warrant the extra complexity.
I believe you need to compare the CHARACTER_MAXIMUM_LENGTH or CHARACTER_OCTET_LENGTH metadata values in the INFORMATION_SCHEMA.COLUMNS table instead of using LEN(). This can be done using something like:
SELECT T.TABLE_NAME
, C1.COLUMN_NAME, C1.DATA_TYPE, C1.CHARACTER_MAXIMUM_LENGTH
, C2.COLUMN_NAME, C2.DATA_TYPE, C2.CHARACTER_MAXIMUM_LENGTH
FROM INFORMATION_SCHEMA.TABLES T
JOIN INFORMATION_SCHEMA.COLUMNS C1
ON C1.TABLE_CATALOG = T.TABLE_CATALOG
AND C1.TABLE_SCHEMA = T.TABLE_SCHEMA
AND C1.TABLE_NAME = T.TABLE_NAME
AND C1.COLUMN_NAME = 'Column_1'
JOIN INFORMATION_SCHEMA.COLUMNS C2
ON C2.TABLE_CATALOG = T.TABLE_CATALOG
AND C2.TABLE_SCHEMA = T.TABLE_SCHEMA
AND C2.TABLE_NAME = T.TABLE_NAME
AND C2.COLUMN_NAME = 'Column_2'
WHERE T.TABLE_CATALOG = 'myDB'
AND T.TABLE_TYPE = 'BASE TABLE'
AND C1.CHARACTER_MAXIMUM_LENGTH <> C2.CHARACTER_MAXIMUM_LENGTH
The inner joins both limit results to tables having both columns and retrieve the column metadata. The length compare at the end checks for a mismatch.
This assumes character types. You might also want to check DATA_TYPE consistency ("char" vs "varchar" vs "nvarchar") or some of the other precision and scale values for other non-character data types.
To query the data within the columns you need dynamic SQL. I would advise you not to use INFORMATION_SCHEMA (which is for compatibility only) and instead use sys.tables etc. You don't need to check sys.columns twice, you can use aggregation in the EXISTS subquery to check for multiple columns.
To compare the columns, you can do Column_1 <> Column_2, but that will not deal with nulls correctly. If the columns can be nullable then you should instead use the syntax shown in the code below: NOT EXISTS (SELECT Column_1 INTERSECT SELECT Column_2)
DECLARE #sql nvarchar(max);
SELECT
STRING_AGG(CAST('
SELECT
Table_Name = ' + QUOTENAME(t.name, '''') + ',
Column_1,
Column_2
FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + '
WHERE NOT EXISTS (SELECT Column_1 INTERSECT SELECT Column_2)
' AS nvarchar(max)), '
UNION ALL
' )
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
AND s.name = 'myDB'
WHERE EXISTS (SELECT 1
FROM sys.columns c
WHERE c.object_id = t.object_id
AND c.name IN ('Column_1', 'Column_2')
HAVING COUNT(*) = 2
AND COUNT(DISTINCT c.system_type_id) = 1 -- all same type
);
PRINT #sql; -- your friend
EXEC sp_executesql #sql;

Search database for table with 2 or more specified column names

I have the following query that I use very frequently to find a table in a database that has a specified column name:
SELECT Table_Name, Column_Name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_CATALOG = 'db' AND COLUMN_NAME = 'col_A'
I'm now trying to find a table that has both of the specified columns in the query (ex: both col_A and col_B). I thought it would have been as simple as just further qualifying the WHERE clause, but that was to no avail. Any tips?
Another way that satisfies the "2 or more" requirement without major modifications:
;WITH input(ColumnName) AS
(
SELECT y FROM (VALUES
/* simply list your columns here: */
(N'col_A'),
(N'col_B')
) AS x(y)
)
SELECT t.name FROM input
INNER JOIN sys.columns AS c ON c.name = input.ColumnName
INNER JOIN sys.tables AS t ON c.[object_id] = t.[object_id]
GROUP BY t.name HAVING COUNT(*) = (SELECT COUNT(*) FROM input);
Example db<>fiddle
And FWIW why I don't use INFORMATION_SCHEMA.
As long as you know the database already, this should work for you:
select t.TABLE_NAME
from INFORMATION_SCHEMA.TABLES t
inner join INFORMATION_SCHEMA.COLUMNS c
on t.TABLE_NAME = c.TABLE_NAME
and c.COLUMN_NAME = 'col_A'
inner join INFORMATION_SCHEMA.COLUMNS c2
on t.TABLE_NAME = c2.TABLE_NAME
and c2.COLUMN_NAME = 'col_B'
If you want all column names and tables names that have both columnname you can do
SELECT Table_Name, Column_Name,TABLE_CATALOG
FROM INFORMATION_SCHEMA.COLUMNS
WHERE EXISTS( SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = 'testdb' AND column_name = 'col_a')
AND EXISTS( SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_CATALOG = 'testdb' AND column_name = 'col_b')

Select data for columns that are only in another table

I have two tables, table1 and table2, that have columns common to both.
The queries that obtain the column names of each table are given below,
Query to get columns from first table:
select column_name from information_schema.columns
where table_schema = 'schema1'
and table_name = 'table1';
Query to get columns from second table:
select column_name from information_schema.columns
where table_schema = 'schema2'
and table_name = 'table2';
I need to select data from table2, only the columns which are also in table1.
I do not have a postrgesql intace at the moment but Dynamic SQL is what you need.
The following query will get you the column names that appear in both table 1 and table 2.
select string_agg(column_name, ',') FROM (
select column_name from information_schema.columns
where table_schema = 'schema1'
and table_name = 'table1'
intersect
select column_name from information_schema.columns
where table_schema = 'schema2'
and table_name = 'table2'
)
And you need to build
EXECUTE 'select ' || select string_agg(column_name, ',') FROM (
select column_name from information_schema.columns
where table_schema = 'schema1'
and table_name = 'table1'
intersect
select column_name from information_schema.columns
where table_schema = 'schema2'
and table_name = 'table2'
) || ' from schema2.table2 '
Sory if there any syntax error as writing from mobile app, you can join both result set to get common data.
select column_name from information_schema.columns T2
JOIN (select column_name from information_schema.columns where table_schema = 'schema1' and table_name = 'table1') T1
ON T2.COLUMN_NAME = T1.COLUMN_NAME Where T2.table_schema = 'schema2' and T2.table_name = 'table2';

Oracle how to grab columns for just one index?

I am using the following query to grab the index columns on a table along with their data type:
SELECT DISTINCT COL.COLUMN_NAME, COL.DATA_TYPE
FROM DBA_IND_COLUMNS IND
INNER JOIN DBA_TAB_COLUMNS COL
ON ( IND.TABLE_OWNER = COL.OWNER AND IND.TABLE_NAME = COL.TABLE_NAME
AND IND.COLUMN_NAME = COL.COLUMN_NAME)
WHERE IND.TABLE_NAME = 'MY_TABLE' AND TABLE_OWNER = 'SCHEMA'
But how can I grab the columns for just one index, instead of the columns for all the indexes?
For example:
If a table has indexes:
INDEX1: column_a,column_b
INDEX2: column_c,column_d
My current query would result in:
column_a, varchar
column_b, varchar
column_c, varchar
column_d, varchar
but I want it to result in just:
column_a, varchar
column_b, varchar
Since you just want the columns for the first index (in alphabetical order), you can try:
SELECT DISTINCT COL.COLUMN_NAME, COL.DATA_TYPE
FROM DBA_IND_COLUMNS IND
INNER JOIN DBA_TAB_COLUMNS COL
ON ( IND.TABLE_OWNER = COL.OWNER AND IND.TABLE_NAME = COL.TABLE_NAME
AND IND.COLUMN_NAME = COL.COLUMN_NAME)
WHERE IND.TABLE_NAME = 'MY_TABLE' AND TABLE_OWNER = 'SCHEMA'
AND INDEX_NAME =
(
SELECT MIN(INDEX_NAME)
FROM DBA_IND_COLUMNS IIND
INNER JOIN DBA_TAB_COLUMNS CCOL
ON ( IIND.TABLE_OWNER = CCOL.OWNER AND IIND.TABLE_NAME = CCOL.TABLE_NAME )
)
If you need columns in the first index of the table, try filtering with a subquery:
SELECT DISTINCT COL.COLUMN_NAME, COL.DATA_TYPE
FROM DBA_IND_COLUMNS IND
INNER JOIN DBA_TAB_COLUMNS COL
ON ( IND.TABLE_OWNER = COL.OWNER AND IND.TABLE_NAME = COL.TABLE_NAME
AND IND.COLUMN_NAME = COL.COLUMN_NAME)
WHERE IND.TABLE_NAME = 'MY_TABLE' AND TABLE_OWNER = 'SCHEMA'
AND INDEX_NAME = (SELECT MIN(INDEX_NAME)
FROM DBA_IND_COLUMNS IND2
WHERE IND2.TABLE_NAME = IND.TABLE_NAME
AND IND2.TABLE_OWNER = IND.TABLE_OWNER)
This can even be re-written with some analytic function.

Oracle select index columns along with data type?

I am using the following SQL to grab index columns for a table:
SELECT DISTINCT COLUMN_NAME
FROM DBA_IND_COLUMNS
WHERE TABLE_NAME = 'MY_TABLE' AND TABLE_OWNER = 'SCHEMA'";
I want to adjust this SQL such that I grab the index columns and their data type:
SELECT DISTINCT COLUMN_NAME, DATA_TYPE
FROM DBA_IND_COLUMNS
WHERE TABLE_NAME = 'MY_TABLE' AND TABLE_OWNER = 'SCHEMA'";
But this gives an invalid identifier error for "DATA_TYPE". Is there a way to do this without creating another query?
You need to add DBA_TAB_COLUMNS to your query:
SELECT DISTINCT COL.COLUMN_NAME, COL.DATA_TYPE
FROM DBA_IND_COLUMNS IND
INNER JOIN DBA_TAB_COLUMNS COL
ON ( IND.TABLE_OWNER = COL.OWNER AND IND.TABLE_NAME = COL.TABLE_NAME AND IND.COLUMN_NAME = COL.COLUMN_NAME)
WHERE IND.TABLE_NAME = 'MY_TABLE' AND TABLE_OWNER = 'SCHEMA'