Dears,
I have a table as shown below as a sample, and I want to run one query by which i can find all the yellow highlighted ones by using %AAA%.
Instead of running the Where command on each column one by one, I can do one general find option and it will list all the rows.
Thank you in advance!!
You can include all the conditions in one where clause using or:
where col1 like '%aaa%' or
col2 like '%aaa%' or
. . . -- and so on for all the columns
Unpivot the columns and do a WHERE based on that:
select *
from Table
where exists (select 1
from (values (col1), (col2), (col3) ) AS v (allCols) -- etc
where v.allCols like '%aaa%'
);
If you can't be bothered to type them out, try this little query:
select STRING_AGG('(' + c.name + ')', ', ')
from sys.columns c
where c.object_id = OBJECT_ID('Name_Of_Table_Here');
If you are using sql server then you can write dynamic query to do so. Please try below query:
declare #sql as varchar(max);
select #sql = 'select * from [TableName] where '
+ stuff((
select ' or [' + [column_name] + '] like ''%AAA%'''
from information_schema.columns
where table_name = 'TableName'
for xml path('')
)
, 1, 5, ''
);
exec(#sql);
This query will return every row in which at least one column contains AAA.
If you are using PostgreSQL, you can use its JSON functionality:
select t.*
from the_table t
where exists (select *
from jsonb_each(to_jsonb(t)) as x(col,val)
where val like '%AAA%');
If you are using Postgres 12 or later you can use a SQL/JSON path expression:
select t.*
from the_table t
where to_jsonb(t) ## '$.* like_regex "AAA" flag "i"'
Related
I want to write a dynamic script that removes duplicates. I want to try and avoid a CURSOR so I've been looking into writing strings instead that will have table in one column and corresponding table attributes in another. I have also tried dynamic SQL using WITH. But this is what I have so far. This I intend to use as parameters in dynamic SQL later on
STUFF example. However this results in repeating the same column names for every row:
select name as table_name,
stuff(( select ', ' +char(10)+ ac.[name] FROM DW.sys.columns ac
inner join DW.sys.tables t on ac.object_id=t.object_id
where ac.name not in ('ModifiedOn','ValidFrom','ValidTo')
FOR XML PATH('')
), 1, 1, '')
from sys.tables
What I want is this output:
TableName || ColumnName
table1 || aa,ab,ac
table2 || ba,bb,bc
table3 || ca,cb,cc
My idea is to use this to this effect or similair:
'WITH DELETEDUPLICATE AS (
SELECT '+#ColumnName+',
ROW_NUMBER() OVER(PARTITION BY '+#ColumnName+' ORDER BY '+#ColumnName+') AS Duplicate_Row_Count
FROM '+#TableName+'
)
DELETE
FROM DELETEDUPLICATE
WHERE Duplicate_Row_Count > 1
Any ideas appreciated!
UPDATE:
With satishcse's suggestion i get the table I wanted. I had problem with getting multiple rows in the dynamic WITH step so I just removed that part as a varaible (removed away 'SET #WITH =' ). But how to execute every row? what i get now is:
WITH DELETEDUPLICATE AS(....
For every table per row
In OpenQuery you have to run the query using execute() function. The answer can solve your problem, but I do not suggest you use OpenQuery.
declare #query as nvarchar(max)
set
#query =
'WITH DELETEDUPLICATE AS (
SELECT '+#ColumnName+',
ROW_NUMBER() OVER(PARTITION BY '+#ColumnName+' ORDER BY '+#ColumnName+') AS Duplicate_Row_Count
FROM '+#TableName+'
)
DELETE
FROM DELETEDUPLICATE
WHERE Duplicate_Row_Count > 1'
execute(#query)
try the following for the first part:
select name as table_name,
stuff(( select ', ' +char(10)+ ac.[name] FROM DW.sys.columns ac
inner join DW.sys.tables t on ac.object_id=t.object_id
where ac.name not in ('ModifiedOn','ValidFrom','ValidTo')
and st.name = t.name
order by 1
FOR XML PATH('')
), 1, 1, '')
from sys.tables st
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
I am trying to get the dynamic column names from the table using the 'INFORMATION_SCHEMA.COLUMNS' Following is the query.
Select COLUMN_NAME into #TempTable
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'MyTable'
Result:
COLUMN_NAME
Person_ID
Person_Name
Person_Address
Wanting to Do:
Select * from MyTable where Person_ID = 1
What can be the ways to use the Person_ID from 1st query to the second query?
You can use dynamic SQL to execute this via the EXEC command.
Build a VARCHAR string for your query based on the dynamic column names you are getting from your first query, then EXEC on the string you have created.
You have not provided enough information on exactly what columns you need in your WHERE clause, or how you determine which ones, but dynamic SQL seems to be what you need here.
if you are trying to do something like this
select * from [table] where [col] =#param
then you can use query like below
declare #query nvarchar(max)
select
#query='select * from '+t.name +
' where '+c.name + ' ='+
case
when c.name ='Person_ID' then '1'
when c.name ='Someother_ID' then '10'
else c.name
end
from sys.tables t join sys.columns c
on c.object_id=t.object_id
and t.name ='MyTable'
exec( #query)
Currently I have prepared a sting of columns which can be added (hence the need for the dynamic query.
I have #cols which can be printed with an output like "Color","Size","Width"
I then have a SELECT/COUNT statement which needs to look like as follows...
SELECT
Product_code,
count(distinct [Color]),
count(distinct [Size]),
count(distinct [Width])
FROM.....
I need of the columns that I have in my string of columns to be counted with distinct..
Also would be even better if I could add a AS with the name of each of these in here too!
Many help is much appreciated - my SQL are OK but the dynamic bit turns me blue!
Cheers.
Convert your comma seperated list to a table first. See this
Assuming that table name to be ListOfColumns
DECLARE #Query VARCHAR( 1000 ) = '';
SELECT #Query+=#Query + ', COUNT(DISTINCT ' + COLUMN_NAME + ') AS ' + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
INNER JOIN ListOfColumns d ON c.COLUMN_NAME = d.ColName
WHERE TABLE_NAME = 'MyTable';
SET #Query = 'SELECT ' + STUFF( #query,1,3,'' ) + ' FROM Tbl';
EXEC ( #Query );
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)