Return records based on the result of a function - sql

I have records as follows:
1) BEL
1) MERSEN
A) VISHAY-SPRAGUE
CIRCUIT PARTNERS
BENTEK
CIRCUIT TEST
I want to return a distinct set where if the record has a closing bracket, then remove the entire bracket prefix ( 1) MERSEN becomes MERSEN) otherwise return the record as is. This is an ad hoc, one off query. I've tried something like this.
IF (CHARINDEX(')', (SELECT [MANUFACTURER] FROM [dbo].[QPL_ITMSUPAC_NF]), 1) > 0)
SELECT DISTINCT SUBSTRING([dbo].[QPL_ITMSUPAC_NF].[MANUFACTURER], 4, 99)
FROM [dbo].[QPL_ITMSUPAC_NF]
ELSE
SELECT DISTINCT [dbo].[QPL_ITMSUPAC_NF].[MANUFACTURER]
FROM [dbo].[QPL_ITMSUPAC_NF]
...but get the error:
Subquery returned more than 1 value...
The above was in a procedure.
Thoughts?

Use exists and move the function into the subquery. In this case, the charindex() is equivalent to like ')%':
IF (EXISTS (SELECT 1 FROM [dbo].[QPL_ITMSUPAC_NF] WHERE MANUFACTURER LIKE ')%') )
SELECT DISTINCT SUBSTRING([dbo].[QPL_ITMSUPAC_NF].[MANUFACTURER], 4, 99)
FROM [dbo].[QPL_ITMSUPAC_NF]
ELSE
SELECT DISTINCT [dbo].[QPL_ITMSUPAC_NF].[MANUFACTURER]
FROM [dbo].[QPL_ITMSUPAC_NF]

You could do it with using right function, with taking help from len and charindex functions:
select right(MANUFACTURER,len(MANUFACTURER)-charindex(')',MANUFACTURER))
from QPL_ITMSUPAC_NF
Example:
select right('1)a sample name',len('1)a sample name')-charindex(')','1)a sample name'))
Output:
a sample name

your subquery SELECT [MANUFACTURER] FROM [dbo].[QPL_ITMSUPAC_NF] is returning more than 1 value (a table) but the charindex() function is expecting only 1 value.

SELECT DISTINCT LTRIM(RIGHT(MANUFACTURER,LEN(MANUFACTURER) - CHARINDEX(')',MANUFACTURER)))
FROM QPL_ITMSUPAC_NF
...returned 2552 records
IF (EXISTS (SELECT 1 FROM [dbo].[QPL_ITMSUPAC_NF] WHERE MANUFACTURER LIKE ')%') )
SELECT DISTINCT LTRIM(SUBSTRING([dbo].[QPL_ITMSUPAC_NF].[MANUFACTURER], CHARINDEX(')', [MANUFACTURER], 1) + 1, 99))
FROM [dbo].[QPL_ITMSUPAC_NF]
ELSE
SELECT DISTINCT [dbo].[QPL_ITMSUPAC_NF].[MANUFACTURER]
FROM [dbo].[QPL_ITMSUPAC_NF]
...returned 2495 records.
Close enough for my needs, thanks.

Related

Impala SQL Query

Error Message :
select list expression not produced by aggregation output (missing
from GROUP BY clause?): CASE WHEN (flag = 1) THEN date_add(lead_ctxdt,
-1) ELSE ctx_date END lot_endt
code :
select c.enrolid, c.ctx_date, c.ctx_regimen, c.lead_ctx, c.lead_ctxdt, min(c.ctx_date) as lot_stdt,
case when (flag = 1 ) then date_add(lead_ctxdt, -1)
else ctx_date
end as lot_endt
from
(
select p.*,
case when (ctx_regimen <> lead_ctx) then 1
else 0
end as flag
from
(
select a.*, lead(a.ctx_regimen, 1) over(partition by enrolid order by ctx_date) as lead_ctx,
lead(ctx_date, 1) over (partition by enrolid order by ctx_date) as lead_ctxdt
from
(
select enrolid, ctx_date, group_concat(distinct ctx_codes) as ctx_regimen
from lotinfo
where ctx_date between ctx_date and date_add(ctx_date, 5)
group by enrolid, ctx_date
) as a
) as p
) as c
group by c.enrolid, c.ctx_date, c.ctx_regimen, c.lead_ctx, c.lead_ctxdt
I want to get the lead_ctx date minus one as the date when the flag is 1
So i found the answer by executing a couple of times the minor changes. Let me tell you, that when you are trying to min or max alongside you have group_conact in the same query then in Impala this doesn't work. You have to write it in two queries per one more sub query and the min() of something in the outer query or vice versa.
Thank you #dnoeth for letting me understand I have the answer with me already.

How to do a sub-query in SQL

My table looks something like this:
I want to retrieve all the PractitionerIdFK if they have SpecialityIdFK = 1 AND SpecialityIdFK= 2. I tried the following but it doesn't seem to work.
SELECT PractitionerSpecialities.PractitionerIdFK
FROM PractitionerSpecialities
WHERE PractitionerSpecialities.SpecialityIdFK IN (
SELECT PractitionerSpecialities.SpecialityIdFK
FROM PractitionerSpecialities
WHERE PractitionerSpecialities.SpecialityIdFK = 1
AND PractitionerSpecialities.SpecialityIdFK = 2
)
You can use GROUP BY and HAVING:
SELECT ps.PractitionerIdFK
FROM PractitionerSpecialities ps
WHERE ps.SpecialityIdFK IN (1, 2)
GROUP BY ps.PractitionerIdFK
HAVING COUNT(*) = 2; -- the size of the comparison list
This assumes that there are no duplicates in PractitionerSpecialities. If that is a possibility, then use HAVING COUNT(DISTINCT ps.SpecialityIdFK) = 2.
It can be achieved by using IN and BETWEEN operator in SQL .
SELECT PractitionerSpecialities.PractitionerIdFK
FROM PractitionerSpecialities
WHERE PractitionerSpecialities.SpecialityIdFK in (1,2)
-- You can BETWEEN Clause as well ..
SELECT PractitionerSpecialities.PractitionerIdFK
FROM PractitionerSpecialities
WHERE PractitionerSpecialities.SpecialityIdFK BETWEEN 1 AND 2
In Sub query use OR operator instead of AND .

Return 1 if value is 0 without CASE statement

I'm trying to find a tidier way of doing the below query so that I'm not duplicating my code.
SELECT CASE WHEN <COMPLICATED CODE THAT RETURNS A SINGLE INT> = 0
THEN 1
ELSE <COMPLICATED CODE THAT RETURNS A SINGLE INT> END
Ideally, I would like something like this using an existing function rather than creating my own:
SELECT ISVALUE(COMPLICATED CODE THAT RETURNS A SINGLE INT,0,1)
You can use apply:
SELECT (CASE WHEN v.val = 0 THEN 1 ELSE v.val END)
FROM . . . CROSS APPLY
(VALUES (<COMPLICATED CODE THAT RETURNS A SINGLE INT>)) v(val);
You could also do a series of functions:
select coalesce(nullif(<COMPLICATED CODE THAT RETURNS A SINGLE INT>, 0), 1)
However, I think apply is clearer. In addition the above will turn NULL values into 1 as well as 0.
You can use a CTE (or a subquery) as
WITH CTE AS
(
SELECT <COMPLICATED CODE THAT RETURNS A SINGLE INT> AS Value
FROM ...
)
SELECT CASE WHEN Value = 0 THEN 1 ELSE Value END
FROM CTE
This way you write the complicated code just once, and then use just the Value column.
You can use IIF:
SELECT IIF(1 = 1, 'true', 'false')
https://learn.microsoft.com/en-us/sql/t-sql/functions/logical-functions-iif-transact-sql
use a sub query, then you can figure out a mathematical formula that acts to give the values you desire, that way you can eliminate actual boolean logic and replace with mathematical functions
an example is
SELECT *,(1 - ceiling(cos(atan(abs(cast(x as float)))) -
floor(cos(atan(abs(cast(x as float))))))) +
x * ceiling(cos(atan(abs(cast(x as float)))) -
floor(cos(atan(abs(cast(x as float)))))) as Computed
FROM
( select 0 as x union SELECT 1 X UNION SELECT -.123 UNION SELECT 9.1 UNION SELECT 67000 union select -1) OQ

MS SQL does not return the expected top row when ordering by DIFFERENCE()

I have noticed strange behaviour in some SQL code used for address matching at the company I work for & have created some test SQL to illustrate the issue.
; WITH Temp (Id, Diff) AS (
SELECT 9218, 0
UNION
SELECT 9219, 0
UNION
SELECT 9220, 0
)
SELECT TOP 1 * FROM Temp ORDER BY Diff DESC
Returns 9218 but
; WITH Temp (Id, Name) AS (
SELECT 9218, 'Sonnedal'
UNION
SELECT 9219, 'Lammermoor'
UNION
SELECT 9220, 'Honeydew'
)
SELECT TOP 1 *, DIFFERENCE(Name, '') FROM Temp ORDER BY DIFFERENCE(Name, '') DESC
returns 9219 even though the Difference() is 0 for all records as you can see here:
; WITH Temp (Id, Name) AS (
SELECT 9218, 'Sonnedal'
UNION
SELECT 9219, 'Lammermoor'
UNION
SELECT 9220, 'Honeydew'
)
SELECT *, DIFFERENCE(Name, '') FROM Temp ORDER BY DIFFERENCE(Name, '') DESC
which returns
9218 Sonnedal 0
9219 Lammermoor 0
9220 Honeydew 0
Does anyone know why this happens? I am writing C# to replace existing SQL & need to return the same results so I can test that my code produces the same results. But I can't see why the actual SQL used returns 9219 rather than 9218 & it doesn't seem to make sense. It seems it's down to the Difference() function but it returns 0 for all the record in question.
When you call:
SELECT TOP 1 *, DIFFERENCE(Name, '')
FROM Temp l
ORDER BY DIFFERENCE(Name, '') DESC
All three records have a DIFFERENCE value of zero, and hence SQL Server is free to choose from any of the three records for ordering. That is to say, there is no guarantee which order you will get. The same is true for your second query. Actually, it is possible that the ordering for the same query could even change over time. In practice, if you expect a certain ordering, you should provide exact logic for it, e.g.
SELECT TOP 1 *
FROM Temp
ORDER BY Id;

Regex pattern inside REPLACE function

SELECT REPLACE('ABCTemplate1', 'Template\d+', '');
SELECT REPLACE('ABC_XYZTemplate21', 'Template\d+', '');
I am trying to remove the part Template followed by n digits from a string. The result should be
ABC
ABC_XYZ
However REPLACE is not able to read regex. I am using SQLSERVER 2008. Am I doing something wrong here? Any suggestions?
SELECT SUBSTRING('ABCTemplate1', 1, CHARINDEX('Template','ABCTemplate1')-1)
or
SELECT SUBSTRING('ABC_XYZTemplate21',1,PATINDEX('%Template[0-9]%','ABC_XYZTemplate21')-1)
More generally,
SELECT SUBSTRING(column_name,1,PATINDEX('%Template[0-9]%',column_name)-1)
FROM sometable
WHERE PATINDEX('%Template[0-9]%',column_name) > 0
You can use substring with charindex or patindex if the pattern being looked for is fixed.
select SUBSTRING('ABCTemplate1',1, CHARINDEX ( 'Template' ,'ABCTemplate1')-1)
My answer expects that "Template" is enough to determine where to cut the string:
select LEFT('ABCTemplate1', CHARINDEX('Template', 'ABCTemplate1') - 1)
Using numbers table..
;with cte
as
(select 'ABCTemplate1' as string--this can simulate your table column
)
select * from cte c
cross apply
(
select replace('ABCTemplate1','template'+cast(n as varchar(2)),'') as rplcd
from
numbers
where n<=9
)
b
where c.string<>b.rplcd
Using Recursive CTE..
;with cte
as
(
select cast(replace('ABCTemplate21','template','') as varchar(100)) as string,0 as num
union all
select cast(replace(string,cast(num as varchar(2)),'') as varchar(100)),num+1
from cte
where num<=9
)
select top 1 string from cte
order by num desc