SQL Join with partial string match - sql

I have a table 'TableA' like:
ID Group Type
1 AB SomeValue
2 BC SomeValue
Another table 'TableB' like:
Product Subgroup Type
A XX-AB-XX-text SomeValue
B XX-BC-XY-text SomeValue
I am using INNER JOIN between two tables like:
SELECT DISTINCT ID
FROM TableA TA
INNER JOIN TableB TB
ON TA.Type=TB.Type
I want to add another condition for join, which looks for value of 'Group' in 'Subgroup' and only joins if the 'Group' Value matches after 'XX-' and before '-XX'.
In other words, join only if Group 'AB' shows up at the correct place in Subgroup column.
How can I achieve this? I am using MSSQL

Try this:
SELECT (DISTINCT ID)
FROM TableA TA
INNER JOIN TableB TB
ON TA.Type=TB.Type AND TB.SubGroup LIKE '__-' + TA.Group + '%'

You can use LIKE with CONCAT to build the expression, see this fiddle:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=d7bb4781488e53c31abce0f38d0aaef4
SELECT *
FROM TableA TA
JOIN TableB TB on (
TA.types = TB.types AND
TB.subgroup LIKE concat('XX-', TA.groups, '-XX%')
);
I am dumping all the returned data so you can see it, but you would want to modify that to return only DISTINCT TA.id.
It is possible to build the expression using + instead of CONCAT:
'XX-' + TA.groups + '-XX%'
Also, I would very strongly warn you against using column names that are reserved words. Both GROUP and TYPE are in use by the SQL engine, so to use them you would have to escape them every time, and they might also cause confusion to anybody reading or maintaining your code.

Related

Remove duplicates from result in sql

i have following sql in java project:
select distinct * from drivers inner join licenses on drivers.user_id=licenses.issuer_id
inner join users on drivers.user_id=users.id
where (licenses.state='ISSUED' or drivers.status='WAITING')
and users.is_deleted=false
And result i database looks like this:
And i would like to get only one result instead of two duplicated results.
How can i do that?
Solution 1 - That's Because one of data has duplicate value write distinct keyword with only column you want like this
Select distinct id, distinct creation_date, distinct modification_date from
YourTable
Solution 2 - apply distinct only on ID and once you get id you can get all data using in query
select * from yourtable where id in (select distinct id from drivers inner join
licenses
on drivers.user_id=licenses.issuer_id
inner join users on drivers.user_id=users.id
where (licenses.state='ISSUED' or drivers.status='WAITING')
and users.is_deleted=false )
Enum fields name on select, using COALESCE for fields which value is null.
usually you dont query distinct with * (all columns), because it means if one column has the same value but the rest isn't, it will be treated as a different rows. so you have to distinct only the column you want to, then get the data
I suspect that you want left joins like this:
select *
from users u left join
drivers d
on d.user_id = u.id and d.status = 'WAITING' left join
licenses l
on d.user_id = l.issuer_id and l.state = 'ISSUED'
where u.is_deleted = false and
(d.user_id is not null or l.issuer_id is not null);

Joining 2 tables using the select value from the result of a CASE

I have 1 table where I have to show the value of 1 ID if another ID is null.
The result of that CASE I then need to use to join with a second table that has the same ID as pid.
I'm stuck and can't quite figure this out. Can someone assist in helping me correct this so it works?
My query is below:
SELECT
fa.name, fa.email_address,
CASE
WHEN pl.main_ID IS NULL THEN pl.sub_ID
ELSE pl.main_ID
END as pid ,pl.*
FROM TableA pl
INNER JOIN TableB fa ON fa.parent_account_sid = pl.pid
Use a lateral join (i.e. apply):
SELECT fa.name, fa.email_address, v.pid, pl.*
FROM TableA pl CROSS APPLY
(VALUES (COALESCE(pl.main_ID, pl.sub_ID)) v(pid) JOIN
TableB fa
ON fa.parent_account_sid = v.pid;
Of course, you might find the COALESCE() to be simple enough that just repeating it isn't an issue.

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

Filter a SQL Server table dynamically using multiple joins

I am trying to filter a single table (master) by the values in multiple other tables (filter1, filter2, filter3 ... filterN) using only joins.
I want the following rules to apply:
(A) If one or more rows exist in a filter table, then include only those rows from the master that match the values in the filter table.
(B) If no rows exist in a filter table, then ignore it and return all the rows from the master table.
(C) This solution should work for N filter tables in combination.
(D) Static SQL using JOIN syntax only, no Dynamic SQL.
I'm really trying to get rid of dynamic SQL wherever possible, and this is one of those places I truly think it's possible, but just can't quite figure it out. Note: I have solved this using Dynamic SQL already, and it was fairly easy, but not particularly efficient or elegant.
What I have tried:
Various INNER JOINS between master and filter tables - works for (A) but fails on (B) because the join removes all records from the master (left) side when the filter (right) side has no rows.
LEFT JOINS - Always returns all records from the master (left) side. This fails (A) when some filter tables have records and some do not.
What I really need:
It seems like what I need is to be able to INNER JOIN on each filter table that has 1 or more rows and LEFT JOIN (or not JOIN at all) on each filter table that is empty.
My question: How would I accomplish this without resorting to Dynamic SQL?
In SQL Server 2005+ you could try this:
WITH
filter1 AS (
SELECT DISTINCT
m.ID,
HasMatched = CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END,
AllHasMatched = MAX(CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END) OVER ()
FROM masterdata m
LEFT JOIN filtertable1 f ON join_condition
),
filter2 AS (
SELECT DISTINCT
m.ID,
HasMatched = CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END,
AllHasMatched = MAX(CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END) OVER ()
FROM masterdata m
LEFT JOIN filtertable2 f ON join_condition
),
…
SELECT m.*
FROM masterdata m
INNER JOIN filter1 f1 ON m.ID = f1.ID AND f1.HasMatched = f1.AllHasMatched
INNER JOIN filter2 f2 ON m.ID = f2.ID AND f2.HasMatched = f2.AllHasMatched
…
My understanding is, filter tables without any matches simply must not affect the resulting set. The output should only consist of those masterdata rows that have matched all the filters where matches have taken place.
SELECT *
FROM master_table mt
WHERE (0 = (select count(*) from filter_table_1)
OR mt.id IN (select id from filter_table_1)
AND (0 = (select count(*) from filter_table_2)
OR mt.id IN (select id from filter_table_2)
AND (0 = (select count(*) from filter_table_3)
OR mt.id IN (select id from filter_table_3)
Be warned that this could be inefficient in practice. Unless you have a specific reason to kill your existing, working, solution, I would keep it.
Do inner join to get results for (A) only and do left join to get results for (B) only (you will have to put something like this in the where clause: filterN.column is null) combine results from inner join and left join with UNION.
Left Outer Join - gives you the MISSING entries in master table ....
SELECT * FROM MASTER M
INNER JOIN APPRENTICE A ON A.PK = M.PK
LEFT OUTER JOIN FOREIGN F ON F.FK = M.PK
If FOREIGN has keys that is not a part of MASTER you will have "null columns" where the slots are missing
I think that is what you looking for ...
Mike
First off, it is impossible to have "N number of Joins" or "N number of filters" without resorting to dynamic SQL. The SQL language was not designed for dynamic determination of the entities against which you are querying.
Second, one way to accomplish what you want (but would be built dynamically) would be something along the lines of:
Select ...
From master
Where Exists (
Select 1
From filter_1
Where filter_1 = master.col1
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_1
)
Intersect
Select 1
From filter_2
Where filter_2 = master.col2
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_2
)
...
Intersect
Select 1
From filter_N
Where filter_N = master.colN
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_N
)
)
I have previously posted a - now deleted - answer based on wrong assumptions on you problems.
But I think you could go for a solution where you split your initial search problem into a matter of constructing the set of ids from the master table, and then select the data joining on that set of ids. Here I naturally assume you have a kind of ID on your master table. The filter tables contains the filter values only. This could then be combined into the statement below, where each SELECT in the eligble subset provides a set of master ids, these are unioned to avoid duplicates and that set of ids are joined to the table with data.
SELECT * FROM tblData INNER JOIN
(
SELECT id FROM tblData td
INNER JOIN fa on fa.a = td.a
UNION
SELECT id FROM tblData td
INNER JOIN fb on fb.b = td.b
UNION
SELECT id FROM tblData td
INNER JOIN fc on fc.c = td.c
) eligible ON eligible.id = tblData.id
The test has been made against the tables and values shown below. These are just an appendix.
CREATE TABLE tblData (id int not null primary key identity(1,1), a varchar(40), b datetime, c int)
CREATE TABLE fa (a varchar(40) not null primary key)
CREATE TABLE fb (b datetime not null primary key)
CREATE TABLE fc (c int not null primary key)
Since you have filter tables, I am assuming that these tables are probably dynamically populated from a front-end. This would mean that you have these tables as #temp_table (or even a materialized table, doesn't matter really) in your script before filtering on the master data table.
Personally, I use the below code bit for filtering dynamically without using dynamic SQL.
SELECT *
FROM [masterdata] [m]
INNER JOIN
[filter_table_1] [f1]
ON
[m].[filter_column_1] = ISNULL(NULLIF([f1].[filter_column_1], ''), [m].[filter_column_1])
As you can see, the code NULLs the JOIN condition if the column value is a blank record in the filter table. However, the gist in this is that you will have to actively populate the column value to blank in case you do not have any filter records on which you want to curtail the total set of the master data. Once you have populated the filter table with a blank, the JOIN condition NULLs in those cases and instead joins on itself with the same column from the master data table. This should work for all the cases you mentioned in your question.
I have found this bit of code to be faster in terms of performance.
Hope this helps. Please let me know in the comments.

SQL excluding via Join

This may be pretty simple but I'm finding it difficult to wrap my head around this.
Basically, I have 2 tables, a and b. 'b' contains a list of all possible items, and 'a' contains a row which links to an item in 'b', and also a parent number. i.e, to display the rows in a with their information I do something like this:
select a.field1, a.field2, b.description
from a inner join b on a.itemid = b.itemid
where a.parentnumber = #parentnumber
That sort of thing work sfine. But I also want a dropdown box to display all that items that are not listed for that parent account in a. How would I do this?
SELECT *
FROM b
WHERE itemid NOT IN
(
SELECT itemid
FROM a
WHERE a.parentnumber = #parentnumber
)
By using a left join to this subquery, you can give an alias and use this alias to perform a null-check. I prefer this approach because the alias, which contains the results of the subquery, can be used through the whole query.
SELECT *
FROM b
LEFT JOIN
(
SELECT itemid
FROM a
Where a.parentnumber = #parentnumber
) As Sub On b.itemid = sub.itemid
WHERE sub.itemid IS NULL