How to join two table with partial match - sql

I have two table with the following data:
TableA.name
R4.23-Core-2
R4.23-Core-2
LA#213 CGHPBXsw01 127.213 0024-737e-e341
LA#252 CGHRack1sw01 127.252 0022-57ab-d781
SOC-01A-SW01
to - R4-DISTR-9512
to-R2-DISTR-5900-1
to-R3.25-EDGE
TableB.caption
R4.23-Core-2.ehd.ca
R4.23-Core-2.nhd.ca
CGHPBXsw01
CGHRack1sw01
SOC-01A-SW01
R4-DISTR-9512
R2-DISTR-5900-1.phsnc.
R3.25-EDGE.phsne.edjc.ca
I've tried using the following join statement but it doesn't seem to work for any row with a . in it.
dbo.TableA.Name
INNER JOIN dbo.TableB.Caption
ON dbo.TableA.Name LIKE '%' + dbo.TableB.Caption + '%'
I also try using replace function, which work but there are too much variant to include with replace.
I could try using the RIGHT or LEFT function to normalize the data but for row that doesn't have '.' it would throw an error. And I don't know how to skip row that doesn't have '.'
What is the most efficient way to join these two table?

In some situations in your example the caption is longer, and other times the name is longer, if you wanted to join on any value where name is in the caption or caption is in the name you could use:
dbo.TableA.Name
INNER JOIN dbo.TableB.Caption
ON dbo.TableA.Name LIKE '%' + dbo.TableB.Caption + '%'
OR dbo.TableB.Caption LIKE '%' + dbo.TableA.Name + '%'
That could explain why your query isn't working as expected.
As far as the most efficient way to do this, you'd want to have a standardized field in your table that you could use to JOIN on via equality (ex. a.col1 = b.col1), so that would entail stripping out the heart of each field that makes it join-worthy.
Update: If the important part is everything before the first period, then you want to use a combination of LEFT() and CHARINDEX() (and a CASE statement since not all strings contain a period):
SELECT NewField = CASE WHEN CHARINDEX('.',Name) > 0 THEN LEFT(Name,CHARINDEX('.',Name)-1)
ELSE Name
END
FROM YourTable
You could use the above in your JOIN too:
dbo.TableA.Name
INNER JOIN dbo.TableB.Caption
ON CASE WHEN CHARINDEX('.',TableA.Name) > 0 THEN LEFT(TableA.Name,CHARINDEX('.',TableA.Name)-1)
ELSE TableA.Name
END
= CASE WHEN CHARINDEX('.',TableB.Caption) > 0 THEN LEFT(TableB.Caption,CHARINDEX('.',TableB.Caption)-1)
ELSE TableB.Caption
END

How about this ( Not Tested)
dbo.TableA
INNER JOIN dbo.TableB
ON CHARINDEX(dbo.TableB.Caption, dbo.TableA.Name) > 0
Test it and don't forget Upvote OR accept.

Related

Single Query To Select Based On Parameters If Or not supplied

I have used SqlDataSource and have a select query based on District & Zone as below
SELECT a.[committee_id] memberid, a.[membername], a.[memberemail], a.[memberdesignation], a.[membercreatedby],b.districtname AS district,b.districtid,c.zone_name AS zone,c.zoneid
FROM [committee_details] a
LEFT JOIN district_master b on b.districtid=a.districtid
LEFT JOIN zone_master c on c.districtid=a.districtid and c.zoneid = a.zoneid
WHERE (a.[membercreatedby] = 'director') AND ((convert(varchar,a.districtid) LIKE '%2%') AND (convert(varchar,a.zoneid) LIKE '%25%')) ORDER BY a.[committee_id] DESC
It's an inline query. I have tried above query but not able to figure out how to Select condition based.
I want if district supplied then Select according to District, if both District & Zone supplied then Select according to both & If nothing supplied then Select All. But should be in single query. How should I do this?
First, fix your query so you are not using meaningless table aliases. Use table abbreviations! I would also drop all the square braces; they just make the query harder to write and to read.
Basically, you want comparisons with NULL in the WHERE clause. I have no idea why your sample code uses LIKE, particularly columns that appear to be numbers. Nothing in the question explains why LIKE is used for the comparison, so the idea is:
SELECT cd.committee_id as memberid, cd.membername,
cd.memberemail, cd.memberdesignation, cd.membercreatedby,
dm.districtname AS district, dm.districtid,
zm.zone_name AS zone, zm.zoneid
FROM committee_details cd LEFT JOIN
district_master dm
ON cd.districtid = dm.districtid LEFT JOIN
zone_master zm
ON zm.districtid = cd.districtid AND
zm.zoneid = cd.zoneid
WHERE cd.membercreatedby = 'director') AND
(cd.districtid = #district or #district is null) AND
(cd.zoneid = #zone or #zone is null)
ORDER BY cd.[committee_id] DESC;
If you were using LIKE, then I would phrase the logic like:
WHERE cd.membercreatedby = 'director') AND
(cast(cd.districtid as varchar(255)) like #district) AND
(cast(cd.zoneid as varchar(255)) like #zone)
And pass in the patterns as '%' when you want all values to match. This assumes that the columns in cd are not NULL. If they can be NULL, then you want an explicit comparison, as in the first example.
If I got the question right then you can use parameters and compare to the column itself if the values are not supplied or not present.
try the following:
SELECT a.[committee_id] memberid, a.[membername], a.[memberemail], a.[memberdesignation], a.[membercreatedby],b.districtname AS district,b.districtid,c.zone_name AS zone,c.zoneid
FROM [committee_details] a
LEFT JOIN district_master b on b.districtid=a.districtid
LEFT JOIN zone_master c on c.districtid=a.districtid and c.zoneid = a.zoneid
WHERE (a.[membercreatedby] = 'director')
AND b.districtname = isnull(nullif(#districtname, ''), b.districtname)
AND c.zone_name = isnull(nullif(#zone_name, ''), c.zone_name)
ORDER BY a.[committee_id] DESC

Adding WHERE clause to outer query causes inner query to error

The below query worked until I added a WHERE clause.
SELECT
PersonTable.FullName,
View_PersonToHead.DirectorId
FROM PersonTable
LEFT JOIN View_PersonToDirector ON PersonTable.PersonId = View_PersonToDirector.PersonId
WHERE View_PersonToDirector.DirectorId = 12345 --No error if this line is removed
The error message is:
Invalid length parameter passed to the RIGHT function.
This leads me to believe that there was an error with how I wrote the View_PersonToDirector view. Something about adding the WHERE clause causes the query to be evaluated/optimized in a different way, exposing some issue.
Internals of View_PersonToDirector:
WITH items AS (
SELECT
PersonId,
0 AS [Level],
CAST(PersonId AS VARCHAR(255)) AS [Path]
FROM PersonTable
UNION ALL
SELECT
e.PersonId,
[Level] + 1,
CAST([Path] + ' < ' + CAST(e.PersonId AS VARCHAR(255)) AS VARCHAR(255))
FROM PersonTable e
INNER JOIN items itms ON itms.PersonId = e.ManagerId
)
SELECT
A.PersonId,
CASE
WHEN A.[Level] = 1
THEN A.PersonId
ELSE CAST(LEFT(A.PathToDirector, CHARINDEX(' ', A.PathToDirector)) AS INT)
END AS DirectorId
FROM (
SELECT
items.PersonId,
items.[Level],
RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
FROM items
WHERE Path LIKE '1111 < %' --Id of director
) A
I suspect that having a WITH cte in the view causes the query optimizer to work differently, applying the WHERE filtering in a different order. Is this bad practice?
One of your problems is definitely in this code:
RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
I appreciate that you think that this WHERE clause fixes the problem:
WHERE Path LIKE '1111 < %'
But it does not. The problem is that SQL Server does not guarantee the order of evaluation of expressions. This true even for subqueries, CTEs, and views. These can be evaluated in any order. In fact, SQL Server sometimes pushes the evaluation to the node that reads from the table -- which is why you get an error.
Personally, I think this is a bug in SQL Server. Those powers that be do not agree. Here are two solutions:
(CASE WHEN Path LIKE '1111 < %' THEN RIGHT(items.[Path], LEN(items.[Path])-7) END) AS PathToDirector
Or:
(CASE WHEN Path LIKE '1111 < %' THEN RIGHT(items.[Path], LEN(items.[Path] + '1234567')-7) END AS PathToDirector
You may have the same problem with the CHARINDEX().
The use of a where condition related to column from left join table generate and implicit inner join
In this case add the condition to the ON clause for let the left join work
SELECT
PersonTable.FullName,
View_PersonToHead.DirectorId
FROM PersonTable
LEFT JOIN View_PersonToDirector ON PersonTable.PersonId = View_PersonToDirector.PersonId
AND View_PersonToDirector.DirectorId = 12345
for the length problem try use conditional eg using case
SELECT
items.PersonId,
items.[Level],
case when LEN(items.[Path]) > 7 then RIGHT(items.[Path], LEN(items.[Path])-7)
ELSE items.[Path] END AS PathToDirector
FROM items
The base part of your rCTE has:
CAST(PersonId AS VARCHAR(255)) AS [Path]
Which means base rows of the rCTE would contain values such as 1234 which are shorter than 7 characters and cause RIGHT(x, LEN(x) - 7) to fail. The condition:
RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
Could be safely written as:
SUBSTRING(items.[Path], NULLIF(CHARINDEX(' < ', items.[Path]), 0) + 3, LEN(items.[Path]))

how to use LIKE instead of IN with multiple values?

I want to replace In with Like to make the query work.
SELECT
1 AS coddit, COD_CAT AS cam_cod, DES_CAT AS cam_desc,
LIVELLO_CAT AS livello, COD_CAT_PADRE AS cat_padre,
COD_L1, COD_L2, COD_L3, COD_L4, COD_L5, COD_L6
FROM
dbo.CLASS_ART
WHERE
1=1
AND TIPO_CLASS = 16 --B2B
AND LIVELLO_CAT = '0'
AND COD_CAT IN (SELECT DISTINCT CAT_MERCE.COD_CAT
FROM ART_LIST_PREZZI
INNER JOIN ART_ANA ON ART_LIST_PREZZI.COD_ART = ART_ANA.COD_ART
INNER JOIN CAT_MERCE ON ART_ANA.COD_CAT = CAT_MERCE.COD_CAT
AND ART_LIST_PREZZI.COD_LIST = 'EXPORT_002')
The comparison I would like to do with LIKE otherwise the query doesn't work well
the subquery returns more than one value and it is correct but if I use Like instead of IN I have this error message:
Query return more than 1 values
Using LIKE against a subquery that returns multiple records won't work. A solution would be to turn the IN condition to an EXISTS condition, like:
and exists (
select 1
from ART_LIST_PREZZI
inner join ART_ANA
on ART_LIST_PREZZI.COD_ART = ART_ANA.COD_ART
inner join CAT_MERCE
on ART_ANA.COD_CAT = CAT_MERCE.COD_CAT
and ART_LIST_PREZZI.COD_LIST = 'EXPORT_002'
where COD_CAT like '%' + CAT_MERCE.COD_CAT + '%'
)
Like has to be compared to a single string, so you need to set all your ids on a single string. You can do that using the for xml clause.
(SELECT DISTINCT CAST(CAT_MERCE.COD_CAT AS VARCHAR(32))
FROM ART_LIST_PREZZI
INNER JOIN ART_ANA ON ART_LIST_PREZZI.COD_ART = ART_ANA.COD_ART
INNER JOIN CAT_MERCE ON ART_ANA.COD_CAT = CAT_MERCE.COD_CAT
AND ART_LIST_PREZZI.COD_LIST = 'EXPORT_002'
FOR XML PATH(''))
Now I would delimite your ids by commas, so you don't find false positives, and compare it using like.
AND
(SELECT DISTINCT ',' + CAST(CAT_MERCE.COD_CAT AS VARCHAR(32)) + ','
FROM ART_LIST_PREZZI
INNER JOIN ART_ANA ON ART_LIST_PREZZI.COD_ART = ART_ANA.COD_ART
INNER JOIN CAT_MERCE ON ART_ANA.COD_CAT = CAT_MERCE.COD_CAT
AND ART_LIST_PREZZI.COD_LIST = 'EXPORT_002'
FOR XML PATH('')) LIKE '%,' + COD_CAT + ',%'
This would work, and you would have changed your IN operator with a LIKE operator, but I don't see the point of it, its performance would be worse than your original query.
Use EXISTS:
EXISTS (SELECT 1
FROM ART_LIST_PREZZI LP JOIN
ART_ANA A
ON LP.COD_ART = A.COD_ART JOIN
CAT_MERCE M
ON A.COD_CAT = M.COD_CAT AND
LP.COD_LIST = 'EXPORT_002' AND
CLASS_ART.COD_CAT LIKE M.COD_CAT
)
I assume that the logic you actually want uses wildcards:
CLASS_ART.COD_CAT LIKE CONCAT('%', M.COD_CAT, '%')
If so, it suggests an issue with the data model. Why would two columns with the same name (COD_CAT) need to be joined using LIKE instead of =.

SQL LIKE is only working With Full Name

In one of my sp I have following lines of code
select distinct (a.itemid), a.itemcode, v.itemdescription
from aitem a
INNER JOIN vwitemdescription v ON a.itemID = v.itemID
WHERE a.active=1
-----------------------------------------
AND (#ItemDesc like '%'+ a.itemdescription +'%')
-----------------------------------------
If I give #ItemDesc value in full description, I get values and if I give #ItemDesc value in half description I get nothing in return.
For Example :
If I giv
#ItemDesc = 'Cow Dung - '
I get result as
---------------------------------------
|itemid | itemcode | itemdescription |
--------------------------------------
| 63 | 40-17005 | Cow Dung - |
---------------------------------------
And even if I cut the string as #ItemDesc = 'Cow Dung' I get the same results,
But if I cut it into #ItemDesc = 'Cow' or only to single character I don't get any results.
I want to load the item even if I enter only a single charecter in it.
Is there anything wrong with my code? How to get it right?
You need to switch the items in your LIKE expression:
AND a.itemdescription LIKE '%' + #ItemDesc + '%'
Using this logic, any substring of the itemdescription would match. For example the following is a true condition:
AND `Cow Dung - ` LIKE '%Cow%'
Here is the full query:
SELECT DISTINCT
a.itemid, a.itemcode, v.itemdescription
FROM aitem a
INNER JOIN vwitemdescription v
ON a.itemID = v.itemID
WHERE
a.active = 1 AND
a.itemdescription LIKE '%' + #ItemDesc + '%';
Comparison variable should be always at the right side of the statement. Your new query should be as below.
select distinct (a.itemid), a.itemcode, v.itemdescription
from aitem a
INNER JOIN vwitemdescription v ON a.itemID = v.itemID
WHERE a.active=1
--changed condition start
AND (a.itemdescription like '%'+ #ItemDesc +'%')
--changed condition end

Getting "replicated" values

So I have two tables: Agent and Conta.
Agents can have a lot of Conta but one Conta can only have one Agent.
I want to display just the Agents, to create a html table where you can see all of the agents present in the database - on a backoffice!
I'm using the following code, but the result I'm getting is one agent entry for each conta where he participated.
WITH LAST_COUNT AS
(SELECT {Conta}.[AgentId], {Conta}.[Data], {Conta}.[Valor],{Conta}.[Submit]
FROM {Conta})
SELECT
{Agent}.[Id], {Agent}.[Nome], {Agent}.[Apelido], LAST_COUNT.[Data],
LAST_COUNT.[Valor], LAST_COUNT.[Submit], {AgentPicture}.[Id],
{AgentPicture}.[Filename], {Agent}.[Telemovel], {Agent}.[UserId]
FROM {Agent}
LEFT JOIN {AgentPicture} ON {AgentPicture}.[Id] = {Agent}.[Id]
INNER JOIN LAST_COUNT ON LAST_COUNT.[AgentId] = {Agent}.[Id]
WHERE {Agent}.[Id] LIKE '%' + #SearchFilter + '%'
OR {Agent}.[Nome] LIKE '%' + #SearchFilter + '%'
OR {Agent}.[Apelido] LIKE '%' + #SearchFilter + '%'
OR #SearchFilter = ''
Can you help me? thanks!
In your query you are joining Agent table with LAST_COUNT common table expression (CTE). As a result you have every record from Contra in your results.
Just remove
WITH LAST_COUNT AS
(SELECT {Conta}.[AgentId], {Conta}.[Data], {Conta}.[Valor],{Conta}.[Submit]
FROM {Conta})
from your query and drop the last JOIN statement: LEFT JOIN LAST_COUNT ON LAST_COUNT.[AgentId] = {Agent}.[Id]
Also, do not forget to remove the following columns, which are retrieved from LAST_COUNT table:
LAST_COUNT.[Data],
LAST_COUNT.[Valor], LAST_COUNT.[Submit],