How to check if a value exists in any of the columns in a table in sql - sql

Say, I have 100 columns in a table. I do not know in which columns a particular value could exist. So, I would like to check across all columns, if it exists in any of the 100 columns, I would like to select it.
I searched around a bit, and in most places the solution seems to be something like the following
select *
from tablename
where col1='myval'
or col2='myval'
or col3='myval'
or .. or col100='myval'
I also read a few forums where having to do this is said to be a bad case of database design, I agree, but I'm working on an already existing table in a database.
Is there a more intelligent way to do this?

One way is by reversing the In operator
select *
from yourtable
where 'Myval' in (col1,col2,col3,...)
If you don't want to manually type the columns use dynamic sql to generate the query
declare #sql varchar(max)='select *
from yourtable
where ''Myval'' in ('
select #sql+=quotename(column_name)+',' from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME='yourtable'
select #sql =left(#sql,len(#sql)-1)+')'
--print #sql
exec sp_executesql #sql

Related

How can we use SQL column value as part of sql statement stored in another column

I have a scenario where I need to execute queries on different tables using ETL tool.
I want to store all the required queries in control table.
As part of this, I want to include the column WatermarkValue as part of the value in the column Source_Query, so that I can dynamically use it for my execution. This is how my control table should look like.
Table Name: Metadata_Table
TableID
Source_Query
WatermarkValue
1
select * from dbo.cust_eventchanges where lastmodifieddate >{WatermarkValue}
2022-10-09T12:00:00
2
select * from dbo.cust_contacts where lastmodifieddate >{WatermarkValue}
2022-07-08T03:20:00
So when I run my metadata table like this
select * from Metadata_Table where TableID=1
the result should be like below.
select * from dbo.cust_eventchanges where lastmodifieddate >'2022-10-09T12:00:00'
I know we can do this by concatenating two columns. But I would like to know if this is achievable.
I couldn't able to figure out how to achieve this. Hence, I need help on this scenario
Using sp_executesql with a typed parameter definition reduces the risk of SQL Injection
Example below shows how to run one of your queries. You could simply wrap this in a cursor where each iteration executes a different query in the metadata table.
DROP TABLE IF EXISTS #MetaData_Table
GO
CREATE TABLE #MetaData_Table
(TableID INT,Source_Query NVARCHAR(MAX),WatermarkValue DATETIME)
INSERT INTO #MetaData_Table
(TableID,Source_Query,WatermarkValue)
VALUES
(1,'select * from dbo.cust_eventchanges where lastmodifieddate >#WatermarkValue','2022-10-09T12:00:00'),
(2,'select * from dbo.cust_contacts where lastmodifieddate >#WatermarkValue','2022-07-08T03:20:00')
SELECT * FROM #MetaData_Table
DECLARE #dtVariable DATETIME;
DECLARE #SQLString NVARCHAR(500);
DECLARE #ParmDefinition NVARCHAR(500);
-- You can put this in a cursor to loop through all your tables, this is hardcoded to one for simplicity.
SELECT #SQLString = Source_Query, #dtVariable = WatermarkValue FROM #MetaData_Table WHERE TableID = 1
SET #ParmDefinition = N'#WatermarkValue DATETIME'
EXECUTE sp_executesql #SQLString, #ParmDefinition,
#WatermarkValue = #dtVariable;
You can create a view and use a view in your ETL tool instead of the table.
create view vMetadataQueries
as
select TableID, Source_Query + WatermarkValue as [ExecuteQuery]
from Metadata_Table
This is not a particularly good way of doing that because it potentially leaves you open to SQL injection.

Test for a column within a Select statement

Is it possible to test for a column before selecting it within a select statement?
This may be rough for me to explain, I have actually had to teach myself dynamic SQL over the past 4 months. I am using a dynamically generated parameter (#TableName) to store individual tables within a loop (apologize for the vagueness, but the details aren't relevant).
I then want to be able to be able to conditionally select a column from the table (I will not know if each table has certain columns). I have figured out how to check for a column outside of a select statement...
SET #SQLQuery2 = 'Select #OPFolderIDColumnCheck = Column_Name From INFORMATION_SCHEMA.COLUMNS Where Table_Name = #TABLENAME And Column_Name = ''OP__FolderID'''
SET #ParameterDefinition2 = N'#TABLENAME VARCHAR(100), #OPFolderIDColumnCheck VARCHAR(100) OUTPUT'
EXECUTE SP_EXECUTESQL #SQLQuery2, #ParameterDefinition2, #TABLENAME, #OPFolderIDColumnCheck OUTPUT
IF #OPFolderIDColumnCheck IS NULL
BEGIN
SET #OP__FOLDERID = NULL
END
ELSE
IF #OPFolderIDColumnCheck IS NOT NULL
BEGIN
...etc
but id like to be able to do it inside of a select statement. Is there a way to check and see if OP__FOLDERID exists in the table?
Id like to be able to do something like this:
SELECT IF 'OP__FOLDERID' EXISTS IN [TABLE] THEN 'OP__FOLDERID' FROM [TABLE]
Thank you for any help or direction you can offer.
I'm afraid there isn't any direct way to do this within a SELECT statement at all. You can determine if a column exists in a table, however, and construct your dynamic SQL accordingly. To do this, use something like this:
IF COL_LENGTH('schemaName.tableName', 'columnName') IS NOT NULL
BEGIN
-- Column Exists
END
You could then set a variable as a flag, and the code to construct the dynamic SQL would construct the expression with/without the column, as desired. Another approach would be to use a string value, and set it to the column name if it is present (perhaps with a prefix or suffix comma, as appropriate to the expression). This would allow you to save writing conditionals in the expression building, and would be particularly helpful where you have more than one or two of these maybe-columns in a dynamic expression.

How do I insert the results from "EXEC()" in a temp table

I need som help with a problem, our company have a vendor that deliver a database to us. inside that database, the vendor has a table with alot of t sql scripts. What i want to do is the following, i want to make a select to find the script and then execut the script and store the result in a variable or temp tabel. I can not alter the script from the vedor, so I need the result into something i can manupilate. Another problem is that i dont know how many columns ther result will have. So it has to be flexible. Like one script have 5 columns and and the next script has 8 and so on.
exsample:
DECLARE #SQL nvarchar(MAX) = ( Select distinct script_details
from scripttable where .......)
This will give me the script I want to use, then I use
EXEC(#SQL)
to execute the script.
Then my problem is, the result from this I want into a variable or a table.
I have tryed to make a temp table like this:
create table #TmpTblSP (col1 varchar(MAX),col2 varchar(MAX),col3 varchar(MAX),col4 varchar(MAX),col5 varchar(MAX),col6 varchar(MAX),col7 varchar(MAX),col8 varchar(MAX),col9 varchar(MAX),col10 varchar(MAX),col11 varchar(MAX),col12 varchar(MAX))
then
insert into #TmpTblSP
EXEC(#SQL)
This gives me the following error:
Msg 213, Level 16, State 7, Line 1
Column name or number of supplied values does not match table definition.
But if i know how many columns there are and specify that into the insert it works.
insert into #TmpTblSP(Col1,Col2,Col3)
EXEC(#SQL)
But here you se my problem, I dont know how many columns there are in every script. I could make one script for every script the vendor has, but that will be alot, it's like 3000 scripts in that table and they change them often.
You could try something like:
DECLARE #SQL nvarchar(MAX) = (
Select distinct script_details
into #temptbl
from scripttable where .......
);
EXEC(#SQL);
If you don't know how many columns yous #sql gives then the only solution is use SELECT INTO. I use it in this way:
DECLARE #QRY nvarchar(MAX) = ( Select distinct script_details
from scripttable where .......)
SET #sql = 'SELECT * into ' + #temptablename + ' FROM (' + #qry + ') A '
It gives some flexibility
Remember that it is easy to check structure of the table created in this way in sys so you can build another #SQL from this info if needed.
I this as well recommended to split "SELECT INTO" to 2 parts
One is
SELECT INTO ......... WHERE 1=2
Second
INSERT INTO SELECT ......
Creation of table locks all DB. So it is good to create it as fast as possible and then insert into it.

Querying the same table for a list of databases in MS SQL Server

This is my first time posting on SO, so please go easy!
I'm attempting to write a SQL script that queries the same table for a list of databases in a single SQL Server instance.
I have successfully queried the list of databases that I required using the following, and inserting this data into a temp table.
Select name Into #Versions
From sys.databases
Where name Like 'Master%'
Master is suffixed with numerical values to identify different environments.
Select * From #Versions
Drop Table #Versions
The table name I am trying to query, is the same in each of the databases, and I want to extract the newest value from this table and insert it into the temp table for each of the database names returned.
I have tried researching this but to no avail. I am fairly comfy with SQL but I fear I could be out of my depth here.
You can do the following. Once you have the list of your databases, you can build up the query (you need to edit it for your purpose).
Select name Into #Versions
From sys.databases
Where name Like 'test%'
declare #sql as varchar(max) = ''
select #sql = #sql + 'INSERT INTO sometable SELECT TOP 1 * FROM ' + name + '..sourcetable ORDER BY somedate DESC; '
FROM #Versions
exec (#sql)
Drop Table #Versions
Look at The undocumented sp_MSforeachdb procedure and here

Finding number of columns returned by a query

How can I get the number of columns returned by an SQL query using SQL Server?
For example, if I have a query like following:
SELECT *
FROM A1, A2
It should return the total number of columns in table A1 + total number of columns in table A2. But the query might be more complicated.
Here is one method:
select top 0
into _MYLOCALTEMPTABLE
from (your query here) t
select count(*)
from Information_Schema.Columns c
where table_name = '_MYLOCALTEMPTABLE'
You can do something similar by creating a view.
You didn't specify your SQL Server version but I'm assuming it's not 2012. However, future readers of this question might be on 2012+ so I'm posting this answer for them.
SQL Server 2012 provides a set of procedures to provide more meta-data about queries and parameters. In this case, the stored procedure sp_describe_first_result_set will provide a handy tabular form.
There is also a DMO function, sys.dm_exec_describe_first_result_set, to provide similar content which is what you'd want to use in your example
DECLARE
-- Your query goes here
#query nvarchar(4000) = N'SELECT * FROM mdm.tblStgBatch AS TSB';
-- Tabular results
EXECUTE sys.sp_describe_first_result_set #tsql = #query;
-- Simple column count
SELECT
COUNT(1) AS column_count
FROM
sys.dm_exec_describe_first_result_set(#query, NULL, 0);
The new metadata discovery options are replacing FMTONLY which is how one would solve this problem prior to 2012. My TSQL chops are apparently not strong enough to do anything useful with it and instead I'd have to bail out to a .NET language to work with the output of FMTONLY.
SET FMTONLY ON;
SELECT *
FROM A1, A2;
SET FMTONLY OFF;
Try this;
--Insert into a temp table (this could be any query)
SELECT *
INTO #temp
FROM [yourTable]
--Select from temp table
SELECT * FROM #temp
--List of columns
SELECT COUNT(name) NumOfColumns FROM tempdb.sys.columns WHERE object_id =
object_id('tempdb..#temp');
--drop temp table
DROP TABLE #temp
Ugly I know:
SELECT COUNT(*) +
(
SELECT COUNT(*)
FROM information_schema.columns
WHERE table_name = 'A1'
)
FROM information_schema.columns
WHERE table_name = 'A2'