how to use LIKE instead of IN with multiple values? - sql

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 =.

Related

How to join two table with partial match

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.

Conversion failed when converting the varchar value '_1' to data type int

Trying to run this query I wrote to return a list of files in a course, but running into this error that a conversion failed when converting the varchar value '_1' to data type int.
SELECT cm.course_id, cc.title title, cc.dtmodified dtmodified, MIN(xf.FILE_SIZE) file_size, MIN(crl.resource_id) resource_id, MIN(xu.FULL_PATH) full_path
FROM BBLEARN.dbo.cms_resource_link crl
INNER JOIN BBLEARN.dbo.course_contents cc ON cc.pk1 = crl.parent_pk1
INNER JOIN BBLEARN.dbo.course_main cm ON cm.pk1 = crl.crsmain_pk1
INNER JOIN BBLEARN.dbo.course_users cu ON cm.pk1 = cu.crsmain_pk1
INNER JOIN BBLEARN.dbo.users u ON cu.users_pk1 = u.pk1
INNER JOIN BBLEARN_CMS_DOC.dbo.XYF_FILES xf ON crl.resource_id = xf.ENTRY_ID + '_1'
INNER JOIN BBLEARN_CMS_DOC.dbo.XYF_URLS xu ON xu.FILE_ID = xf.FILE_ID
INNER JOIN BBLEARN_CMS_DOC.dbo.XYF_FILES xf2 ON xu.PARENT_ID = xf2.FILE_ID
WHERE crl.parent_data_type='content'
AND cu.role='P'
AND crl.storage_type='PUBLIC'
AND xf.FILE_TYPE_CODE='F'
AND (LOWER(xu.FILE_NAME) LIKE N'%.mov%' or LOWER(xu.FILE_NAME) LIKE N'%.avi%' or LOWER(xu.FILE_NAME) LIKE N'%.wm%' or LOWER(xu.FILE_NAME) LIKE N'%.mp%' or LOWER(xu.FILE_NAME) LIKE N'%.rm%' or LOWER(xu.FILE_NAME) LIKE N'%.flv%' or LOWER(xu.FILE_NAME) LIKE N'%.amr%' or LOWER(xu.FILE_NAME) LIKE N'%.aif%' or LOWER(xu.FILE_NAME) LIKE N'%.aup%' or LOWER(xu.FILE_NAME) LIKE N'%.m4%' or LOWER(xu.FILE_NAME) LIKE N'%.wav%' or LOWER(xu.FILE_NAME) LIKE N'%.swf%')
AND xu.FULL_PATH NOT LIKE '/internal/%'
AND NOT EXISTS (
SELECT 1 FROM BBLEARN.dbo.cms_resource_link
WHERE crsmain_pk1 = crl.crsmain_pk1
AND parent_pk1 = crl.parent_pk1
AND parent_data_type='content'
AND resource_id = xf2.ENTRY_ID + '_1' )
GROUP BY cm.course_id, cc.pk1, cc.title, cc.dtmodified ORDER BY 1;
Which shows up in line 7 and 21 I believe where there is the concatenation of xf.ENTRY_ID + '_1' and xf2.ENTRY_ID + '_1'
Since xf2.ENTRY_ID is an INT, SQL will not try to concatenate the values, but add them together. You need to explicitly cast the INT to a VARCHAR, or CHAR in order to concatenate them.
Change
AND resource_id = xf2.ENTRY_ID + '_1'
To
AND resource_id = CONVERT(VARCHAR(20),xf2.ENTRY_ID) + '_1'
And
INNER JOIN BBLEARN_CMS_DOC.dbo.XYF_FILES xf ON crl.resource_id = xf.ENTRY_ID + '_1'
To
INNER JOIN BBLEARN_CMS_DOC.dbo.XYF_FILES xf ON crl.resource_id = CONVERT(VARCHAR(20),xf.ENTRY_ID) + '_1'
I am defining a VARCHAR(20) as ENTRY_ID could be of BIGINT data type (though i think you should have way too much rows in order to need 20 digit long ids...
try this.You should cast xf.ENTRY_ID and xf2.ENTRY_ID in varchar type with appropirate range right now im using Max length for the same. Hope your code will work.
Like this:
cast(xf.ENTRY_ID varchar(max)) + '_1' and cast(xf2.ENTRY_ID as varchar(max)) + '_1'

MS SQL Conditional Join

I have a SQL Query :
SELECT * FROM Customer c
LEFT JOIN Invoice I ON I.InvcNum = C.Cust_InvcNum
some thing changed and the join does not work because there is no consistency in the data in the 'Cust_InvcNum'. So now when it does not find the record for the join or if it is null it needs to check for another condition.
LEFT JOIN Invoice I ON I.InvcNum = (SELECT p.InvcPrefix FROM Prefix WHERE p.DealerID = I.DealrID ) + '-' + I.InvcNum
second join I do works but it is taking too long for it get me data. Is there any other way to do this.
Earlier it used to be
Join on I.InvcNum = C.Cust_InvcNum
both the columns has the same data like DlrCd-InvcNum i.e both the columns 1234-A789
but not it could match on the above data or now the column 'InvcNum' in invoice table
can be populated like Dlrd-InvcNum or InvcPrefix-InvcNum
So InvcNum = 1234-A789 but CustNum = I94-A789
so we need to check if the for InvoicePrefix-InvcNum
SELECT *
FROM Customer c
LEFT JOIN (
SELECT i.InvcNum, p.InvcPrefix + '-' + I.InvcNum AS pInvcNum
FROM Invoice i LEFT JOIN Prefix p ON i.DealrID = p.DealrID
) ip ON c.Cust_InvcNum = CASE WHEN c.Cust_InvcNum = ip.InvcNum
THEN ip.InvcNum
ELSE ip.pInvcNum END

Trouble with case statement

I am having problems with this case statement. I don't know what I am doing wrong but I get the error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
I have a case when the field equals a value then do a left outer join but if the field equals a different value then do a inner join.
This is my query:
SELECT
case
when oqt = '26' then
(Select qm.id_oqm, cast(isNull(id_eval, '') as varChar(50)) + ' - ' + qm.methodName as methodName, qm.methodName as actualMethod,cv.*
FROM OQMethods QM left outer join courseversions cv on cv.evalid = QM.id_eval and cv.courselanguage = 'EN' and cv.courseactive='Y' and cv.id_cp > 0
WHERE QM.id_oqt in (SELECT tempoq.oqt FROM tempoq INNER JOIN OQMethods ON tempoq.oqt = OQMethods.id_oqt)and active = 1)
END,
case
when oqt = '31' then
(Select qm.id_oqm, cast(isNull(id_eval, '') as varChar(50)) + ' - ' + qm.methodName as methodName, qm.methodName as actualMethod,cv.*
FROM OQMethods QM inner join courseversions cv on cv.evalid = QM.id_eval and cv.courselanguage = 'EN' and cv.courseactive='Y' and cv.id_cp > 0
where QM.id_oqt in (SELECT tempoq.oqt FROM tempoq INNER JOIN OQMethods ON tempoq.oqt = OQMethods.id_oqt) and active = 1)
END
from tempoq
The case is an expression that must evaluate to a value. The Select statements that you have return multiple values.
It would seem that you're trying to use Case like it's a C# switch? If that's the case, then you're likely better off with an IF ELSE IF construction.
It looks like you want to do something like this rather than using a CASE statement.
DECLARE #t int
-- This would contain your oqt value
SET #t =1
IF #t = 1
BEGIN
SELECT * FROM tableA
END
ELSE IF #t = 2
BEGIN
SELECT * FROM TableB
END
Select qm.id_oqm, cast(isNull(id_eval, '') as varChar(50)) + ' - ' + qm.methodName as methodName, qm.methodName as actualMethod,cv.*
FROM OQMethods QM
inner join tempoq on tempoq.oqt = QM.id_oqt
left outer join courseversions cv on cv.evalid = QM.id_eval and cv.courselanguage = 'EN' and cv.courseactive='Y' and cv.id_cp > 0
WHERE active = 1 and (tempoq.oqt = '26' or (tempoq.oqt = '31' and courseversions.* is not null))
left outer join means join OQMethods's data which even no match data from courseversions,
then filter the data with null courseversions.* that is inner join.
Hope I have the right understanding.

T-SQL Nested Subquery

Not being a SQL expert, and also only being semi-competent in CTE, how can I code this statement use the resultset from the following subquery within the main query, as our SQL Server is 2000.
declare #subcategoryConcatenate varchar(3999)
set #subcategoryConcatenate = ''
select #subcategoryConcatenate = #subcategoryConcatenate + pumpCategoryName + ',' FROM
(SELECT
SCD.PUMPCATEGORYNAME,
SCD.ENGINECATEGORYNAME,
SCD.DETAILEDDESCRIPTION
FROM PRTTICKHDR PHDR
INNER JOIN BIDHDR BHDR ON PHDR.DELIV_TICKET_NUMBER = BHDR.DTICKET
INNER JOIN PRTTICKITEM PITM ON PHDR.CONNECTION_ID = PITM.CONNECTION_ID AND PHDR.DELIV_TICKET_NUMBER = PITM.DELIV_TICKET_NUMBER
LEFT JOIN SUBCATEGORYDESCRIPTION SCD ON PITM.ITEM = SCD.PUMPCATEGORY
WHERE SCD.pumpCategoryName IS NOT NULL)
subcategoryDescription
select #subcategoryConcatenate
SELECT
PHDR.CONNECTION_ID AS CONNECTION_ID,
BHDR.OFFICE AS OFFICE,
CMP.NAME AS DEPOT,
CMP.ADDR1 AS DEPOT_ADDR1,
CMP.ADDR2 AS DEPOT_ADDR2,
CMP.CITY AS DEPOT_CITY,
CMP.STATE AS DEPOT_STATE,
CMP.ZIP AS DEPOT_ZIP,
CMP.PHONENUM AS DEPOT_PHONE,
CMP.FAXNUM AS DEPOT_FAX,
ACT.NAME AS ACTIVITY,
SAL.SALES_PERSON_NAME AS SALESPERSON,
BHDR.DTICKET AS DELIV_TICKET_NUMBER,
BHDR.PO_NUMBER,
BHDR.CREATED AS CREATED_DATE,
BHDR.DDATE AS ESTIMATED_START_DATE,
BHDR.PROJ_STOP_DATE AS PROJECTED_STOP_DATE,
CUR.ID,
CUR.CODE,
CUR.EXCHANGE_RATE,
CST.TERMS,
BHDR.ORDBY AS ORDERED_BY,
PHDR.ORDERED_BY_CONTACT,
BHDR.ACCT AS ACCOUNT,
BHDR.NAME AS CUSTOMER,
BHDR.ADDR1 AS CUST_ADDR1,
BHDR.ADDR2 AS CUST_ADDR2,
BHDR.CITY AS CUST_CITY,
BHDR.STATE AS CUST_STATE,
BHDR.ZIP AS CUST_ZIP,
PHDR.SHIP_TO_NAME,
PHDR.SHIP_TO_ADDR1,
PHDR.SHIP_TO_ADDR2,
PHDR.SHIP_TO_CITY,
PHDR.SHIP_TO_STATE,
PHDR.SHIP_TO_ZIP,
PITM.PRINT_SEQUENCE,
PITM.ITEM,
PITM.SUBGROUP,
PITM.DESCRIPTION,
SCD.PUMPCATEGORYNAME,
SCD.ENGINECATEGORYNAME,
SCD.DETAILEDDESCRIPTION,
PITM.QUANTITY,
PITM.UNIT_OF_MEASURE,
PITM.BILLING_LOGIC_TYPE,
PITM.INVENTORY_TYPE,
PITM.CHARGEABLE_DAYS,
PITM.MINIMUM_CHARGE,
PITM.WEEKLY_CHARGE,
PITM.MONTHLY_CHARGE,
PITM.UNINVOICED_NET,
PITM.UNINVOICED_VAT
FROM PRTTICKHDR PHDR
INNER JOIN BIDHDR BHDR ON PHDR.DELIV_TICKET_NUMBER = BHDR.DTICKET
INNER JOIN PRTTICKITEM PITM ON PHDR.CONNECTION_ID = PITM.CONNECTION_ID AND PHDR.DELIV_TICKET_NUMBER = PITM.DELIV_TICKET_NUMBER
INNER JOIN COMPANY CMP ON BHDR.OFFICE = CMP.OFFICE
LEFT JOIN SUBCATEGORYDESCRIPTION SCD ON PITM.ITEM = SCD.PUMPCATEGORY
INNER JOIN ACTIVITIES ACT ON BHDR.ACTIVITY_ID = ACT.ID
INNER JOIN SALES_PERSON SAL ON BHDR.SALES_PERSON = SAL.SALES_PERSON
INNER JOIN CUSTOMERS CST ON BHDR.ACCT = CST.CUSTNUM
INNER JOIN CURRENCY CUR ON CST.CURRENCY_ID = CUR.ID
ORDER BY
BHDR.DTICKET,
PITM.PRINT_SEQUENCE
ASC
SQL Server 2000 doesn't support CTEs. Your options are to either make a view out of the subquery if it's used a lot, or to do an inline view:
select
.. stuff..
from
table1 t1
join table2 t2 on ...stuff...
join (
select
...
from
...
where
...
) inline on ... stuff ...
where
....
You need a user-defined function.
From the looks of it, each PRTTICKITEM can have more than one PUMPCATEGORY?
(The question needs to better explain the desired results.)
In that case, your UDF would look something like this:
CREATE FUNCTION GetPumpCategoriesByItem (#ItemID int)
RETURNS varchar (8000)
AS
BEGIN
DECLARE
#CategoryList varchar (8000)
SET #CategoryList = NULL -- MUST be null to avoid leading comma.
SELECT
#CategoryList = COALESCE (#CategoryList + ', ', '') + SCD.PUMPCATEGORYNAME
FROM
SUBCATEGORYDESCRIPTION SCD
WHERE
SCD.PUMPCATEGORY = #ItemID
ORDER BY
SCD.PUMPCATEGORYNAME
RETURN #CategoryList
END
.
To use it would be something like this:
SELECT
PITM.ITEM,
dbo.GetPumpCategoriesByItem (PITM.ITEM),
... ...
FROM
... ...
INNER JOIN PRTTICKITEM PITM ON ... ...
... ...