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).
Related
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 + '%';
Can I use query inside the like operator.
I used the query below, but it returns error.
select *
from customers
where cust_name like '%'||select name from members||'%'
Something like the following should work :
SELECT
*
FROM
customers c
WHERE 1=1
AND EXISTS
(SELECT 1
FROM members m
WHERE 1=1
AND c.cust_name LIKE '%'||m.name||'%'
)
In PLSQL if you want to run a Query then you need to decalre a variable to hold the result of the query. So you can do it as :
DECLARE
var customers%ROWTYPE;
BEGIN
SELECT c.*
INTO var
FROM customers c
INNER JOIN members m ON c.cust_name LIKE '%' || m.name || '%';
END;
it is sufficient to enclose the subquery in parentheses
like as
cust_name like '%'||(select name from members)||'%'
but this only works when the table "members" has only one record
You should define the name part of members in subselect (works with only 1 row):
select *
from customers
where cust_name like (select '%'||name||'%' from members);
Same can be done with JOIN (works with multiple rows):
select C.*
from customers C
INNER JOIN MEMBERS M ON (C.CUST_NAME LIKE '%'||M.NAME||'%');
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
I have the following query:
SELECT top 2500 *
FROM table a
LEFT JOIN table b
ON a.employee_id = b.employee_id
WHERE left(a.employee_rc,6) IN
(
SELECT employeeID, access
FROM accesslist
WHERE employeeID = '#client.id#'
)
The sub select in the where clause can return one or several access values, ex:
js1234 BLKHSA
js1234 HDF48R7
js1234 BLN6
In the primary where clause I need to be able to change the integer expression from 6 to 5 or 4 or 7 depending on what the length of the values returned in the sub select. I am at a loss if this is the right way to go about it. I have tried using OR statements but it really slows down the query.
Try using exists instead:
SELECT top 2500 *
FROM table a LEFT JOIN
table b
ON a.employee_id = b.employee_id
WHERE EXISTS (Select 1
FROM accesslist
WHERE employeeID = '#client.id#' and
a.employee_rc like concat(employeeID, '%')
) ;
I don't see how your original query worked. The subquery is returning two columns and that normally isn't allowed in SQL for an in.
Move the subquery to a JOIN:
SELECT TOP 2500 *
FROM table a
LEFT JOIN table b ON a.employee_id = b.employee_id
LEFT JOIN accesslist al ON al.access LIKE concat('%', a.employee_id)
WHERE al.employeeID = '#client.id#'
Like Gordon, I don't quite see how your query worked, so I'm not quite sure if it should be access or employeeID which is matched.
This construct will enable you to do what you said you want to do, have an integer value depend on somethign from a subquery. It's the general idea only, the details are up to you.
select field1, field2
, case when subqueryField1 = 'fred' then 1
when subqueryField1 = 'barney' then 2
else 3 end integerValue
from table1 t1 join (
select idField subqueryField1, etc
from whereever ) t2 on t1.idFeld = t2.idField
where whatever
Also, a couple of things in your query are questionable. First, a top n query without an order by clause doesn't tell the database what records to return. Second, 2500 rows is a lot of data to return to ColdFusion. Are you sure you need it all? Third, selecting * instead of just the fields you need slows down performance. If you think you need every field, think again. Since the employee ids will always match, you don't need both of them.
I am trying to join one record in a table to another using a derived table and am having a bit of trouble figuring out the correct query to do so. What I want to do is have a JOIN of a derived table to a query where the derived table uses where statements depending on data from the outer query that is being joined to. So here is the current code that I am working on:
SELECT a.viewerid, a.id, v.id AS entry, a.jobid, v.sourceid, v.cost, a.applicant
FROM a_views a,
JOIN (
SELECT TOP 1 id, sourceid, cost FROM a_views vt
WHERE vt.viewerid = a.viewerid
AND vt.viewed_at <= a.viewed_at
AND vt.referrer NOT LIKE '%' + vt.hostName + '%'
ORDER BY viewed_at DESC
) v
The derived table is a query of the same table that the outer query uses, and viewerid is a FK to itself across the table where id is a unique auto-incrementing PK. I need to get the latest record in the a_views table where the viewer id's match, the datestamp (viewed_at) is less than the outer datestamp and the referrer column doesn't contain the hostName column.
Sounds like you need APPLY:
SELECT a.viewerid, a.id, v.id AS entry, a.jobid, v.sourceid, v.cost, a.applicant
FROM a_views a
CROSS APPLY (
SELECT TOP 1 id, sourceid, cost FROM a_views vt
WHERE vt.viewerid = a.viewerid
AND vt.viewed_at <= a.viewed_at
AND vt.referrer NOT LIKE '%' + vt.hostName + '%'
ORDER BY viewed_at DESC
) v
Since your query has JOIN I've gone for CROSS APPLY, but you may need OUTER APPLY depending on your exact requirements.