Using JOIN statement with CONTAINS function - sql-server

In SQL Server database I have a View with a lot of INNER JOINs statements. The last join uses LIKE predicate and that's why it's working too slowly. The query looks like :
SELECT *
FROM A INNER JOIN
B ON A.ID = B.ID INNER JOIN
C ON C.ID1 = B.ID1 INNER JOIN
...........................
X ON X.Name LIKE '%' + W.Name + '%' AND
LIKE '%' + W.Name2 + '%' AND
LIKE '%' + W.Name3 + '%'
I want to use CONTAINS instead of LIKE as :
SELECT *
FROM A INNER JOIN
B ON A.ID = B.ID INNER JOIN
C ON C.ID1 = B.ID1 INNER JOIN
...........................
X ON CONTAINS(X.Name, W.Name) AND
CONTAINS(X.Name, W.Name2) AND
CONTAINS(X.Name, W.Name3)
I know that CONTAINS is working faster than LIKE and also that can't use CONTAINS in JOIN statements.
Is there any workaround in this case or suggestion?
Thanks in advance.

It's not that CONTAINS can't be used in joins.
You just can't use columns as a second parameter of CONTAINS - see MSDN - CONTAINS (Transact-SQL)
CONTAINS
( { column_name | ( column_list ) | * }
,'<contains_search_condition>'
[ , LANGUAGE language_term ]
)
However, you can use a variable as a search condition, so you can use a cursor and then get all data you need.
Here is some very rough example:
declare #Name nvarchar(max)
declare #Temp_A table(Name nvarchar(max))
declare #Temp_B table(Name nvarchar(max))
--=============================================================================================
insert into #Temp_A (Name)
select 'Test'
insert into #Temp_B (Name)
select 'aaaTestaaa'
--=============================================================================================
-- Query 1 - LIKE
--=============================================================================================
select *
from #Temp_A as A
inner join #Temp_B as B on B.Name like '%' + A.Name + '%'
--=============================================================================================
-- Query 2 - CONTAINS
--=============================================================================================
declare table_cursor cursor local fast_forward for
select distinct Name from #Temp_A
open table_cursor
while 1 = 1
begin
fetch table_cursor into #Name
if ##fetch_status <> 0 break
select * from #Temp_B where contains(Name, #Name)
end
close table_cursor
deallocate table_cursor

CONCAT works perfect, I have tested it with PostgreSQL
SELECT *
FROM TABLE_ONE AS a INNER JOIN TABLE_TWO AS b
ON b.field LIKE CONCAT('%', CONCAT(a.field, '%'));
Please refer to similar answer here

You can create a join using a LIKE..
something like this:
SELECT * FROM TABLE_ONE
FULL OUTER JOIN TABLE_TWO ON TABLE_ONE.String_Column LIKE '%' + TABLE_TWO.Name + '%'
ie - select everything from TABLE_ONE where the string_column is contained in the TABLE_TWO name

In short there isn't a way to do this using CONTAINS, it simply is not allowed in a JOIN like this.
see: TSQL - A join using full-text CONTAINS
So although there is performance hit, IMO like is the easiest solution here.

Related

How to create a 'Like' + 'In' query in SQL Server (SSMS)

I'm looking a way to create a query that includes the functionality of both: 'Like' and 'In' keywords together. So with this I'll be able to make a search of multiple sub-strings in one sentence instead of creating multiple consults:
i.e:
Instead of this:
select * from MyTable where ColumnName like'%substring1%'
select * from MyTable where ColumnName like'%substring2%'
select * from MyTable where ColumnName like '%substring3%'
I want to do something like this:
select * from MyTable where ColumnName in ('%substring1%','%substring2%','%substring3%')
Just to clarify: the wild card (%) is not allowed or it doesn't work together with the "in" keyword so it forces me to look for full strings instead of substrings.
Any idea or approach?
The only approach I can think of is the following:
select * from MyTable where ColumnName like '%substring1%' or ColumnName like '%substring2%' or ColumnName like '%substring3%'
If you have additional conditions, just group it.
select * from MyTable where (ColumnName like '%substring1%' or ColumnName like '%substring2%' or ColumnName like '%substring3%') and AnotherColumn='string'
Pre-populate a temp table with the terms you want to search (or table variable, CTE, or whatever). Then join but use like instead of an equalty operator.
select *
from myTable a
inner join #terms b
on a.myField like '%' + b.term + '%'
Yet another option is pass a delimited string
Example
Declare #Search varchar(max) = 'John,Cappelletti,Some Other Value'
Select Distinct A.*
From MyTable A
Join (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace(#Search,',','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) B on charindex(RetVal,ColumnName)>0
Why not just use or operator;
SELECT ColumnName
FROM MyTable
WHERE ColumnName LIKE '%substring1%' OR ColumnName LIKE '%substring2%' OR ColumnName LIKE '%substring3%'

Find all dependencies of a list of stored procedures

We have a list of 51 stored procedures used in our application.
I need to find out the names of all the functions and stored procedures called by those stored procs
Is there a quick way to find out what our stored procedures are calling?
I tried using sys.dm_sql_referencing_entities as below
SELECT
referencing_schema_name +'.'+ referencing_entity_name AS ReferencedEntityName,
referencing_class_desc AS ReferencingEntityDescription
FROM sys.dm_sql_referencing_entities ('dbo.sp_GetPayRunDetails', 'OBJECT');
GO
but I am hoping that there is a way to check all 50 stored procs and get detailed information about what other objects (stored procs, and functions) that they call...
I don't want to do this manually is because I also need recursive dependencies for the stored procs and functions that are called...
This query might do the trick for you:
WITH cteDependencies AS (
SELECT e.referencing_id object_id, e.referencing_id, e.referenced_id, e.referenced_schema_name, e.referenced_entity_name
FROM sys.sql_expression_dependencies e
WHERE e.referencing_id = OBJECT_ID('dbo.sp_GetPayRunDetails')
UNION ALL
SELECT d.object_id, e.referencing_id, e.referenced_id, e.referenced_schema_name, e.referenced_entity_name
FROM sys.sql_expression_dependencies e
JOIN cteDependencies d ON d.referenced_id = e.referencing_id AND e.referenced_id <> e.referencing_id
)
SELECT OBJECT_NAME(d.object_id) source_name, d.*
FROM cteDependencies d
JOIN sys.all_objects o ON d.referenced_id = o.object_id
WHERE o.[type] IN ('P','FN','TF'); -- for a list of types see https://msdn.microsoft.com/en-us/library/ms178618.aspx?f=255&MSPPError=-2147217396
Note that it will fail on circular references; if you have such, you need to track the dependency path (for instance in a XML column) and skip items which are repeated.
You can change the "source" by altering the OBJECT_ID('dbo.sp_GetPayRunDetails') filter to include the SPs you want to analyze.
I had a function. Modify it as per your means. It manages self-references
CREATE FUNCTION GetDependents(
#ObjectName AS SYSNAME
)
RETURNS #result TABLE ( Seq INT IDENTITY, ObjectName SYSNAME, Hierarchy VARCHAR(128) , objNameStr varchar(max) )
AS
BEGIN
;WITH Obj AS (
SELECT DISTINCT s.id AS ParentID, s.DepID AS ObjectID, o1.Name AS ParentName, o2.Name AS ChildName,
QUOTENAME(sch1.name) + '.' + QUOTENAME(o1.Name) + '(' + RTRIM(o1.type) + ')' COLLATE SQL_Latin1_General_CP1_CI_AS
AS ParentObject,
QUOTENAME(sch2.name) + '.' + QUOTENAME(o2.Name) + '(' + RTRIM(o2.type) + ')' COLLATE SQL_Latin1_General_CP1_CI_AS AS ObjectName,
o2.Name as objNameStr
FROM sys.sysdepends s
INNER JOIN sys.all_objects o1 ON s.id = o1.object_id
INNER JOIN sys.schemas sch1 ON sch1.schema_id = o1.schema_id
INNER JOIN sys.all_objects o2 on s.DepID = o2.object_id
INNER JOIN sys.schemas sch2 ON sch2.schema_id = o2.schema_id
where o2.type not in ('u') and s.id <>s.DepID --Self Reference ko hata...
), cte AS (
SELECT
0 AS lvl,
ParentID,
ObjectId,
ParentObject,
ObjectName,
CAST(ObjectID AS VARBINARY(512)) AS Sort
, objNameStr
FROM obj WHERE ParentName = #ObjectName
UNION ALL
SELECT
p.lvl+ 1,
c.ParentID,
c.ObjectId,
c.ParentObject,
c.ObjectName,
CAST(p.sort + CAST(c.ObjectID AS VARBINARY(16))
AS VARBINARY(512)), c.objNameStr
FROM cte p
INNER JOIN obj c ON p.ObjectID = c.ParentID
)
INSERT INTO #result (ObjectName, Hierarchy,objNameStr)
SELECT
ObjectName,
'|-' + REPLICATE('-',(lvl * 4)) + ObjectName,
objNameStr
FROM cte
ORDER BY Sort
OPTION (MAXRECURSION 32767);
RETURN
END

Looking for everywhere a particular value has been used in foreign keys in one pass

Good afternoon.
I am using SQL Server 2008/TSQL. It is probably important to note I do not have write access to any db (I am a read only user). I do not have write access to the db but can insert temp tables if absolutely needed.
I would like to preface what I am about to say with letting everyone know I have no formal education in SQL. Hopefully this makes sense - I may be inaccurate in some vocabulary etc.
Scope of what I am trying to do:
1. Select a specific recordID (value) in a column (primary key) from a table
2. Find where that specific number/recordID is used in all dependents/foreign keys
3. Return the tablename and columnname with a count of how many times that value was found
So, as an example...
You have a table with information on a person tied to a recordID, say something like:
dbo.MemberInfo with RecordID, Name Etc.
The ID number of the member (MemberInfo.RecordID) is used in other
tables, say: dbo.Awards as [HonoreeID]
(dbo.Awards.HonoreeID=MemberInfo.RecordID) dbo.Address as [MemberID]
(dbo.Address.MemberID=MemberInfo.RecordID) dbo.Contact as [PersonID]
(dbo.Contact.PersonID=MemberInfo.RecordID) ...and potentially a few
hundred others
I basically want to run through all the tables and see how many times a particular value/record is in use. Now, to add some complexity to this, it needs to be generic, as the column I may be looking up dependents on may change from day to day. (Ex. I may be looking for dependents of EventID tomorrow)
My current process is:
-Use a select to find the ID of the person I need
-Look at all the foreign keys linked to RecordID (Primary Key) of dbo.Members
-Dump the tablenames and columns of the foreign keys out into Excel
-Do a find and replace to make a bunch of SELECT COUNTS with a WHERE=#Variable
-Put it into SQL, define my variable and set it equal to the initial ID number
There has to be a better way. I have attempted many variations of the following with lots of errors and no success:
--DECLARE #Selected CHAR
SELECT T.Name, C.Name
--SET #Selected=(SELECT T.Name FROM sys.tables T)
--CASE WHEN (T.NAME IS NOT NULL) THEN 1
--ELSE '0' END AS 'MyTrial'
FROM
--sys.tables t
sys.foreign_key_columns AS fk
INNER JOIN sys.tables AS t ON fk.parent_object_id = t.object_id
INNER JOIN sys.columns AS c ON fk.parent_object_id = c.object_id AND fk.parent_column_id = c.column_id
WHERE Referenced_Object_ID=--Insert object ID here
My line of thinking was/is:
1. Query foreign keys used in a table/column, return the table name and column name from the dependent tables
2. Feed these results into something that can build me a new query to return a count of a value in each of the tables/columns where applicable
3. Return the tablename as well, so it can easily be fed into another select statement should I need to look at the details making up the count.
So my results might look something like this:
Tablename, Columnname, Count of Value in Column
Ideally, no value, table name etc. would be returned if the count is less than one.
My process may be extremely flawed out of the gate, but anything offered helps me learn. Thanks!
The script below uses no stored procedures but does use a temporary table. As far as I can conceive, there's no way around it. It runs very quickly for me, although I was working with a relatively small test database.
This is my first venture into dynamic SQL, so I can't testify to the safety of this code against SQL injection attacks and such. I, too, am self-taught.
Declare #Primary_Table varchar(100) = '';
Declare #Column_Name varchar(100) = '';
Declare #Specific_Value int = ;
IF(OBJECT_ID('tempdb..#Selected_Tables') is not null)
Begin
Drop Table #Selected_Tables;
End
Select Distinct T.Name as Table_Name
, C.Name as Column_Name
, CAST(null as bigint) as Referenced_Records
Into #Selected_Tables
FROM sys.foreign_key_columns AS fk
INNER JOIN sys.tables AS t
ON fk.parent_object_id = t.object_id
INNER JOIN sys.columns AS c
ON fk.parent_object_id = c.object_id
AND fk.parent_column_id = c.column_id
Inner Join sys.tables AS t2
ON fk.referenced_object_id = t2.object_id
Inner Join sys.columns AS c2
on fk.referenced_column_id = c2.column_id
Where t2.name = #Primary_Table
and c2.Name = #Column_Name;
Declare #sqlCommand nvarchar(max);
Declare #Unprocessed_Records int = (Select Count(1)
From #Selected_Tables
Where Referenced_Records is null);
Declare #Processing_Table varchar(1000) = (Select Top 1 Table_Name
From #Selected_Tables
Where Referenced_Records is null);
Declare #Processing_Column varchar(1000) = (Select Top 1 Column_Name
From #Selected_Tables
Where Referenced_Records is null
and Table_Name = #Processing_Table);
While #Unprocessed_Records > 0
Begin
Set #sqlCommand = 'Update #Selected_Tables '
+ 'Set Referenced_Records = (Select Count(1) '
+ 'From ' + #Processing_Table + ' '
+ 'Where ' + #Processing_Column + ' = ' + CAST(#Specific_Value as nvarchar(1000)) + ') '
+ 'Where Table_Name = ''' + #Processing_Table + ''' '
+ 'and Column_Name = ''' + #Processing_Column + ''';'
Exec (#sqlCommand);
Set #Unprocessed_Records = (Select Count(1)
From #Selected_Tables
Where Referenced_Records is null);
Set #Processing_Table = (Select Top 1 Table_Name
From #Selected_Tables
Where Referenced_Records is null);
Set #Processing_Column = (Select Top 1 Column_Name
From #Selected_Tables
Where Referenced_Records is null
and Table_Name = #Processing_Table);
End;
Select * From #Selected_Tables;

Dynamic sql to convert column names with one row into table with 2 columns and several rows

After searching for several ways of converting columns to rows using PIVOT, cross join etc my question still goes unanswered
I have a Result set which returns 1 row and 147 columns
ID | Name | DOB | BloodGroup | ...... 147 columns
1 XYZ 17MAY A+ ......
My aim is to convert this result set into 2 columns and 147 rows
Column_Name | Value
ID 1
NAME XYZ
: :
How should I go about it ? I appreciate your feedback
I took the second approach Gordon mentioned in his post, but built dynamic SQL from it. I CROSS JOINED the result of a few sys table JOINs and a source table, then built a CASE statement off the column names. I UNION it all together as dynamic SQL then EXECUTE it. To make it easy, I've made all the variable items into variables which you fill out at the beginning of the routine. Here's the code:
USE AdventureWorks2012;
GO
DECLARE #MySchema VARCHAR(100),
#MyTable VARCHAR(100),
#MyUIDColumn VARCHAR(100),
#MyFieldsMaxLength VARCHAR(10),
#SQL AS VARCHAR(MAX);
SET #MySchema = 'Person';
SET #MyTable = 'Person';
-- Unique ID which routine will identify unique entities by. Will also sort on this value in the end result dataset.
SET #MyUIDColumn = 'BusinessEntityID';
-- This determines the max length of the fields you will cast in your Value column.
SET #MyFieldsMaxLength = 'MAX';
WITH cteSQL
AS
(
SELECT 1 AS Sorter, 'SELECT c.name AS ColumnName,' AS SQL
UNION ALL
SELECT 2, 'CASE' AS Statement
UNION ALL
SELECT 3, 'WHEN c.name = ''' + c.name + ''' THEN CAST(mt.' + c.name + ' AS VARCHAR(' + #MyFieldsMaxLength + ')) '
FROM sys.tables t INNER JOIN sys.columns c
ON t.object_id = c.object_id
WHERE t.name = #MyTable
UNION ALL
SELECT 4, 'END AS Value' AS Statement
UNION ALL
SELECT 5, 'FROM sys.tables t INNER JOIN sys.columns c ON t.object_id = c.object_id INNER JOIN sys.schemas s ON t.schema_id = s.schema_id, ' + #MySchema + '.' + #MyTable + ' mt WHERE t.name = ''' + #MyTable + ''' AND s.name = ''' + #MySchema + ''' ORDER BY mt. ' + #MyUIDColumn + ', c.name;'
)
SELECT #SQL =
(
SELECT SQL + ' '
FROM cteSQL
ORDER BY Sorter
FOR XML PATH ('')
);
EXEC(#SQL);
I really can't say what execution time will be like. I ran it against AdventureWorks2012, Person.Person table (~20k rows, 13 columns) on my local machine and it brought back ~2.5 million rows in about 8 seconds, if that means anything. The good thing is that its flexible to take any table seamlessly. Anyway, just thought it was a fun puzzle so decided to play with it a bit. Hope it helps.
EDIT: Thinking about it, this is probably even slower than Gordon's proposed method, but I did it aready. Oh well. (Yeah, his method works in about half the time. Getting fancy didn't help me much.)
This is called unpivot. The easiest way, conceptually, is to do:
select 'id' as column_name, cast(id as varchar(255)) as column_value
from Result
union all
select 'name', name
from Result
union all
. . .
This can be cumbersome to type. If result is a table, you can use information_schema.columns to create the SQL, something like:
select 'select ''' + column_name + ''' as column_name, cast(' + column_name + ' as varchar(255)) as column_value from result union all'
from information_schema.columns
where table_name = 'Result'
This method is not the most efficient approach, because it requires reading the table for each column. For that unpivot is the better approach. The syntax is described here.
Thanks for the response.
I figured out a way of doing it.
I got all the column names in a comma separated string variable. 2. Passed the same string to the UNPIVOT object. By this approach, hard coding of the 140 column names was completely avoided.

Is there a way to get the fields names of a table created in a function / stored procedure?

when I need the columns of an existing table I use the query:
SELECT c.[name]
FROM
(SELECT * from syscolumns) c
INNER JOIN
(SELECT [id] from sysobjects where name= 'tableName') o on c.[id]=o.[id]
I need the fields of a table that I create during runTime:
select
a.ID,
b.lName,
b.fName
into #T
from
a
inner join
b on a.id=b.id
.
select * from #T_columns
will result a table with 3 rows:
id
lName
fName
How can I do it?
Thanks
When you create a temp table, it will be in tempdb. You can look it up like this:
SELECT COLUMN_NAME
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME LIKE '#T|_%' ESCAPE '|'
If you do a SELECT * FROM INFORMATION_SCHEMA.TABLES in tempdb, you'll see the temp table name you use (#T) actually has a number of underscores appended to it followed by a unique identifier. So you won't find it it you just search where table_name = '#T'.
So that's why you have to use a LIKE as I've shown above. This will match on "#T_" followed by any other other characters.
Try this
SELECT sc.NAME
FROM
tempdb..SYSOBJECTS so JOIN
tempdb..SYSCOLUMNS sc ON sc.id = so.id
WHERE so.NAME LIKE '#T%'

Resources