Passing select result to like operator in SQL - sql

I have results of name in a customer table
I want to find whether this name is present in log table column
My log table changedate column will record name and also id, I want to find only name
select top 100 *
from LogDataTable
where ChangeData like '%'+'select name from customer'+ '%'
But I'm not getting results with this query, any changes I have to do? Can it done?

You can use a join to compare data the way you are asking e.g.
select top 100 L.*
from dbo.LogDataTable L
inner join dbo.Customer C on L.ChangeData like '%' + C.[Name] + '%';
Its not going to perform very well though, as it has to do a table scan of Customer for every row in LogDataTable.
If you were able to ensure that the Customer Name was always the first part of the ChangeData you could use like C.[Name] + '%' and then benefit from indexes.
To solve your collation error, re-collate of one of the columns in your query. I recommend changing C.[Name] since that is already the subject of a table scan. But it depends whether you need an "Accent Sensitive" compare or not e.g.
select top 100 L.*
from dbo.LogDataTable L
inner join dbo.Customer C on L.ChangeData like '%' + C.[Name] collate SQL_Latin1_General_CP1_CI_AS + '%';

Related

How to get the number of rows in a SQL temporary table without using COUNT(*)

A few efficient methods were shown in this post and copied below but that doesn't work for temporary tables.
I really want to avoiding COUNT(*) due to the table size.
The first one below returns and error and the second NULL
SELECT CONVERT(bigint, rows)
FROM sysindexes
WHERE id = OBJECT_ID(#temporaryTable)
AND indid < 2
SELECT SUM (row_count)
FROM sys.dm_db_partition_stats
WHERE object_id=OBJECT_ID(#temporaryTable)
AND (index_id=0 or index_id=1);
It's because you've got to query tempdb, not whatever current database you're in. I filtered the specific table by joining to sysobjects. When doing that, you have to know that #temporaryTable is actually not really named that, but just starts with that name.
select o.name,
rows = CONVERT(bigint, rows)
from tempdb..sysindexes i
join tempdb..sysobjects o on i.id = o.id
where o.name like '#temporaryTable_%'
and indid < 2
select o.name,
row_count = sum(ps.row_count)
from tempdb.sys.dm_db_partition_stats ps
join tempdb..sysobjects o on ps.object_id = o.id
where o.name like '#temporaryTable_%'
and (index_id=0 or index_id=1)
group by o.name
If your main goal is to find the total number of rows, you can perhaps run this:
SELECT MAX(id) FROM sys.dm_db_partition_stats
This returns the last id that was logged and thus will be the number of rows (given that id is incrementing serially).
If the total number of rows is your primary goal
SELECT IDENTITY(INT,1,1) AS ID, FirstName,Surname INTO NewTable FROM ExistingTable;
select max(ID) from NewTable;
Note: When another column inherits the identity property, this does not work.

How to search if a row is a substring of another row of the same column in Oracle

I have a table that contains millions of rows for names of customers as a column. I want to find if a part of a name exists within another row in the same column.
E.g. If a row has value 'Roger Federer' and there are other rows with values, 'Roger' and 'Federer', I want the corresponding primary keys of all the three rows.
You can leverage the use of REGEXP_LIKE
SELECT *
FROM customers
WHERE REGEXP_LIKE (cust_name, 'roger|federer','i')
SQL Fiddle Demo
More examples of REGEXP_LIKE usages can be found here
Another option would be the use of OR
SELECT *
FROM customers
WHERE LOWER(cust_name) LIKE LOWER('%roger%')
OR LOWER(cust_name) LIKE LOWER('%federer%')
SQL Fiddle Demo
Edit
With the use of JOIN, the search string is dynamic. If proper indexes are in place, then it would not have much impact.
SELECT DISTINCT
c1.*
FROM
customers c1
JOIN
customers c2
ON ( LOWER(c1.cust_name) LIKE LOWER(c2.cust_name || '%')
AND c1.cust_id != c2.cust_id)
SQL Fiddle Demo
Edit 2
Perhaps something like the below
SELECT DISTINCT
c1.cust_id,
c1.cust_name,
CASE
WHEN
LOWER(c1.cust_name) LIKE LOWER(c2.cust_name || '%')
THEN
'Matched'
ELSE
'Unmatched'
END
ident
FROM
customers c1
JOIN
customers c2
ON ( LOWER(c1.cust_name) LIKE LOWER(c2.cust_name || '%')
AND c1.cust_id != c2.cust_id)
SQL Fiddle Demo
If you want to construct a logic related to rows, union concepts may suit well,
by the way, in string operations we'd better use collations with patterns through upper or lower functions to satisfy case-insensitivity for letters :
select id from customers where lower(name) like '%roger%' union all
select id from customers where lower(name) like '%federer%';
and no need to add already included complete name ( e.g. Roger Federer ).
Edit :
An Alternative method maybe the following :
select distinct id
from (select lower(regexp_substr('&str', '[^[:space:]-]+', 1, 1)) frst,
lower(regexp_substr('&str', '[^[:space:]-]+', 1, 2)) lst,
lower('&str') nm
from customers) c1
cross join customers c2
where c1.frst like '%' || lower(c2.name) || '%'
or c1.lst like '%' || lower(c2.name) || '%'
or c1.nm like '%' || lower(c2.name) || '%';
by adding a search string('&str') to make the query more dynamic as you wish.
( when prompted enter Roger Federer for str substitution variable )
I think you can use join same table twice (self join) to get output with below query,
select a.*, b.*
from tab1 a
, tab1 b
where ( a.fname like b.fname||'%' or a.lname like b.lname||'%')
and a.id <> b.id

Need only one match from a Left Join

Hello thanks for taking the time to read this.
I have a table TBL_INCIDENT containing columns TXT_INC_ID and TXT_SERVICE, I am using a LEFT JOIN to join it to table TBL_ASMS containing TXT_APPLICATION_ID. The issue I am having is the join will have multiple matches and I only want the first one. I saw an example of code using LIMIT 1, but I am unsure syntactically how it is supposed to be used. I also have seen some solutions for duplication using row_number() over partition, but I could not find one that was a select statement, only deletes.
This is my current state:
SELECT COUNT(A.TXT_INC_ID)
FROM(
SELECT A.TXT_INC_ID, B.APPLICATION_ID
FROM TBL_INCIDENT A
LEFT JOIN TBL_ASMS B ON A.TXT_SERVICE LIKE ('%' || B.APPLICATION_ID || '%') LIMIT 1
)
TXT_INC_ID is the primary key in the table it comes from, and I only want one match per record to be returned by the left join. I am using left because I need every record in table A returned, but only once.
Thanks
Maybe use a Max?
SELECT COUNT(A.TXT_INC_ID)
FROM(
SELECT A.TXT_INC_ID, Max(B.APPLICATION_ID)
FROM TBL_INCIDENT A
LEFT JOIN TBL_ASMS B ON A.TXT_SERVICE LIKE ('%' || B.APPLICATION_ID || '%')
group by A.txt_inc_id
)
Your result is logically the same as:
SELECT COUNT(A.TXT_INC_ID)
FROM TBL_INCIDENT A

SQL NOT LIKE Wildcard Condition on Inner Join

I have a table called hr_grades that contains employee pay grades such as:-
ID hr_grade
1 PR07
2 AC04
I run two stored procedures. One that returns employees whose grades are in this table, and one that does not. These stored procedures carry out a number of different tasks to prepare the data for loading into the end system.
I want the query to carry out a wildcard search on the rows in the grades table. So for example for employees whose grades are in the table
SELECT DISTINCT
Employee_ID,
FROM
#tmp_vo_hr_acp_staff v
INNER JOIN
hr_grades g ON v.hr_grade LIKE g.HR_Grade + '%' -- wildcard search
The reason for the wildcard is that the hr_grades can be like AC04(1) , AC04(2) etc.
This works fine. However I am struggling to get the reverse of this working.
SELECT DISTINCT
Employee_ID,
FROM
#tmp_vo_hr_acp_staff v
INNER JOIN
hr_grades g ON v.hr_grade NOT LIKE g.HR_Grade + '%'
Any ideas how I could get this to wildcard search to work on a NOT LIKE condition?
Change it to
ON NOT (v.hr_grade LIKE g.HR_Grade + '%' )
EDIT:
Removed the ON inside the brackets.
As almost always in SQL, there are some ways to do it:
-- 1. using outer join where the outer table don't match
SELECT DISTINCT Employee_ID
FROM #tmp_vo_hr_acp_staff v
LEFT JOIN hr_grades g ON (v.hr_grade LIKE g.HR_Grade + '%') -- wildcard search
WHERE g.id IS NULL -- use any non nullable column from hr_grades here
-- 2. using EXISTS for set difference
SELECT DISTINCT Employee_ID
FROM #tmp_vo_hr_acp_staff v
WHERE NOT EXISTS (
SELECT 'x' FROM hr_grades g WHERE v.hr_grade LIKE g.HR_Grade + '%'
)
-- 3. using the less popular ANY operator for set difference
SELECT DISTINCT Employee_ID
FROM #tmp_vo_hr_acp_staff v
WHERE NOT v.hr_grade LIKE ANY (
SELECT g.HR_Grade + '%' FROM hr_grades g
)
Personally, I don't like using joins for filtering, so I would probably use option 2. If hr_grades is a much smaller than #tmp_vo_hr_acp_staff, though, you can benefit from using the option 3 (as the set can be determined beforehand and than cached with a single read operation).

How do I find the which table it is belong when I'm only given column name?

Hi I am working on SQL query, a total novice.
So I only have column name, and trying to find which table it is belong to.
How do I find that in toad? Can someone help?
Most databases have tables that describe the contents of the databases. If you are using toad, then I might surmise that you are using Oracle.
If so, you can use:
select *
from syscolumns
where columnname = <whatever you are looking for>
And then lookup the referenceid in systables.
In many other databases, you can use:
select *
from INFORMATION_SCHEMA.columns
where column_name = <whatever you are looking for>
Something like this should work, it will return the column name you search and the associated table
SELECT c.name as columnname, t.name as tablename from sys.columns c
join sys.tables t on c.object_id = T.object_id
where c.name =' put the column you want to find here'