MySQL: Using REPLACE as part of an IN statement - sql

I am trying to use REPLACE to substitute spaces for commas.
If I try SELECT REPLACE('1 2 3 4 5 6 7 86 9', ' ', ', '); then I get exactly what I need in a string.
However, if I try this as part of an IN statement I only get returned the first match (ie, 1)
Here is my entire query:
SELECT aa.category_id,
aa.yyyymm,
aa.cat_balance_ytd
FROM gl_view_cat_balances AS aa
WHERE aa.gl_id = '/JOB//9'
AND aa.fin_years_ago = 0
**AND aa.category_id IN (REPLACE((SELECT detail_2
FROM gl_options
WHERE option_id = 'GLREPFUNCJOB01'),' ', ', '))**
AND aa.yyyymm = (SELECT max(bb.yyyymm)
FROM gl_rep_cat_bals as bb
WHERE bb.gl_unique_id = aa.gl_unique_id
AND bb.category_id = aa.category_id
AND bb.yyyymm <= 200910);
Field detail_2 in record GLREPFUNCJOB01 contains '1 2 3 4 5 6 7 86 9'
If anyone has some helpful hints on how I can get the commas into the string and can use it in the IN I would love to hear about them.

You can't use REPLACE to create a comma delimited list for use in an IN clause. To use that as-is, you'd have to utilize MySQL's Prepared Statements (effectively dynamic SQL) - creating the comma separated list first, and inserting that into the SQL query constructed as a string before executing it.
SELECT a.category_id,
a.yyyymm,
a.cat_balance_ytd
FROM GL_VIEW_CAT_BALANCES a
JOIN GL_OPTIONS o ON INSTR(o.detail2, a.category_id)
AND o.option_id = 'GLREPFUNCJOB01'
JOIN (SELECT b.category_id,
b.gl_unique_id,
MAX(b.yyyymm) 'yyyymm'
FROM GL_REPCAT_BALSs b
WHERE b.yyyymm <= 200910
GROUP BY b.category_id, b.gl_unique_id) x ON x.category_id = a.category_id
AND x.gl_unique_id = a.unique_id
AND x.yyyymm = a.yyyymm
WHERE a.gl_id = '/JOB//9'
AND a.fin_years_ago = 0
Here's an untested, possible non-dynamic SQL alternative, using FIND_IN_SET:
SELECT a.category_id,
a.yyyymm,
a.cat_balance_ytd
FROM GL_VIEW_CAT_BALANCES a
JOIN (SELECT REPLACE(o.detail_2, ' ', ', ') 'detail2_csv'
FROM GL_OPTIONS o
WHERE o.option_id = 'GLREPFUNCJOB01') y ON FIND_IN_SET(a.category, y.detail2_csv) > 0
JOIN (SELECT b.category_id,
b.gl_unique_id,
MAX(b.yyyymm) 'yyyymm'
FROM GL_REPCAT_BALSs b
WHERE b.yyyymm <= 200910
GROUP BY b.category_id, b.gl_unique_id) x ON x.category_id = a.category_id
AND x.gl_unique_id = a.unique_id
AND x.yyyymm = a.yyyymm
WHERE a.gl_id = '/JOB//9'
AND a.fin_years_ago = 0

Gosh, I can't find anything wrong. After fruitlessly desk checking, I created the minimal schema needed and it executes okay, though the tables are empty. Maybe you could show a few records of data?
SELECT aa.category_id, aa.yyyymm, aa.cat_balance_ytd
FROM gl_view_cat_balances AS aa
WHERE aa.gl_id = '/JOB//9' AND
aa.fin_years_ago = 0 AND
aa.category_id IN (REPLACE((SELECT detail_2
FROM gl_options
WHERE option_id = 'GLREPFUNCJOB01'),' ', ', ')) AND
aa.yyyymm = (SELECT max(bb.yyyymm)
FROM gl_rep_cat_bals as bb
WHERE bb.gl_unique_id = aa.gl_unique_id AND
bb.category_id = aa.category_id AND
bb.yyyymm <= 200910);

Thanks for your help, but we ended up creating a temporary table out of the values in glrepfuncjob01 and using that as the sub-select in the IN statement.
Worked a treat.

Related

How to use exists for a complete list of a product?

This is the query to find out if a particular car has W1, W2, WA, WH conditions. How can I modify my query so that I can get a list of all the cars as yes or no for these conditions?
NOTE: Here, I have put v.[carnumber] = 't8302' but I need a complete list.
SELECT
CASE
WHEN EXISTS (
SELECT co.[alias]
FROM [MTI_TAXI].[vehicle] v
LEFT JOIN [MTI_SYSTEM].[Conditions] co with (nolock) on v.DispatchSystemID = co.DispatchSystemID and (v.Conditions & co.conditionvalue > 0)
WHERE co.[alias] in ('W1', 'W2', 'WA', 'WH') and v.[DispatchSystemID] = 6 and v.[CarNumber] = 't8302')
THEN cast ('Yes' as varchar)
ELSE cast ('No' as varchar)
END AS [WATS]
OUTPUT - ( WATS - No )
But, here are all the cars but I am getting yes to WATS condition which is incorrect
enter image description here
Simply utilizing your provided filters and moving the EXISTS to be used in an OUTER APPLY statement:
SELECT
CASE
WHEN [find_wats].[Found] = 1
THEN 'Yes'
ELSE 'No'
END AS [WATS]
FROM
[MTI_TAXI].[vehicle] AS v
OUTER APPLY (SELECT TOP (1)
1 AS [Found]
FROM
[MTI_SYSTEM].[Conditions] AS co
WHERE
v.DispatchSystemID = co.DispatchSystemID
AND
(v.Conditions & co.conditionvalue > 0)
AND
co.[alias] IN ('W1', 'W2', 'WA', 'WH')
AND
v.[DispatchSystemID] = 6) AS [find_wats];
Using this set up, you can then use [find_wats].[Found] = 1 to determine that your record within the table [MTI_TAXI].[vehicle] found a match in [MTI_TAXI].[Conditions] (using your provided criteria) while still maintaining a single record in your final result set for each record originally in the table [MTI_TAXI].[vehicle].
Use count(*) to assert that there was exactly 1 row found by adding:
group by co.[alias]
having count(*) = 1
So the whole query becomes:
SELECT
CASE
WHEN EXISTS (
SELECT co.[alias]
FROM [MTI_TAXI].[vehicle] v
LEFT JOIN [MTI_SYSTEM].[Conditions] co with (nolock)
on v.DispatchSystemID = co.DispatchSystemID
and (v.Conditions & co.conditionvalue > 0)
WHERE co.[alias] in ('W1', 'W2', 'WA', 'WH')
and v.[DispatchSystemID] = 6
and v.[CarNumber] = 't8302'
group by co.[alias]
having count(*) = 1
) THEN cast ('Yes' as varchar)
ELSE cast ('No' as varchar)
end AS [WATS]

concatenating one to many values to one line separated by commas in sql join

I have a join which shares a one to many relationship to one table. I would to in stead of returning this value:
125|PROGRAM1|OEM2|1
125|PROGRAM1|OEM2|2
125|PROGRAM1|OEM2|3
I want to return one line like this:
125|PROGRAM1|OEM2|1,2,3
Here is the sql i am running now:
select d.layout_id,d.pgm_nm,d.corp_nm from io_layout_output d
join (select f.program_nm, c.corp_nm from file_config f
join corp_config c
on f.output_id = c.output_id
where f.output_id = 112) b
on d.pgm_nm = b.program_nm and d.corp_nm = b.corp_nm
How would I end up with the correct output?
You need to use a function for that. See LISTAGG
select d.layout_id,
d.pgm_nm,
LISTAGG(d.corp_nm, ', ') as corp_nm
from io_layout_output d
join (select f.program_nm,
c.corp_nm
from file_config f
join corp_config c
on f.output_id = c.output_id
where f.output_id = 112) b
on d.pgm_nm = b.program_nm
and d.corp_nm = b.corp_nm
group by d.layout_id,
d.pgm_nm
EDIT:
Last time I used Oracle you could use LISTAGG using group by. I just looked at the docs and it doesn't mention it anymore. Here is the way if above does not work:
select d.layout_id,
d.pgm_nm,
LISTAGG(d.corp_nm, ', ')
WITHIN GROUP (ORDER BY d.layout_id, d.pgm_nm) as corp_nm
from io_layout_output d
join (select f.program_nm,
c.corp_nm
from file_config f
join corp_config c
on f.output_id = c.output_id
where f.output_id = 112) b
on d.pgm_nm = b.program_nm
and d.corp_nm = b.corp_nm
Note: I'm just showing how to use the function. Did not look at your query at all. Adding this note because your result data does not match the number of columns on your SQL

Issue with GROUP-BY using alias SELECT

I am using InterBase and struggle to put my query together.
This is my current query:
SELECT a.employee_no, ea.comment, ea.status as EmpadStatus, a.advices_value
FROM advices a
JOIN EMPAD EA ON (A.CODE = EA.CODE and ea.employee_no = A.employee_no ) AND EA.SUPER_FUND_CODE NOT IN ('000038','000113', '', ' ')
JOIN ALLDED ad ON AD.CODE = EA.CODE
WHERE a.employee_no = 13844 and a.advices_date between '1.10.2014' and '31.10.2014'
My result is this:
Employee_No Comment EmpadStatus Advices_value
1 aaa 0 10.20
1 1 30.50
1 bbb 0 69.30
What I need to do is to display Employee_no, Comment and SUM of Advices_Value, but Comment has to be first comment where empad.status = 0.
I was trying to use alias but I know that you cant group by it, so this query is not going to work
SELECT a.employee_no, SUM(a.advices_value), (SELECT DISTINCT ea.comment
FROM EMPAD ea
JOIN allded ad on ea.code = ad.code
WHERE ea.employee_no = a.employee_no and ea.status = 0 and ad.super_type = 1) as comment
FROM advices a
JOIN EMPAD EA ON (A.CODE = EA.CODE and ea.employee_no = A.employee_no ) AND EA.SUPER_FUND_CODE NOT IN ('000038','000113', '', ' ')
JOIN ALLDED ad ON AD.CODE = EA.CODE
WHERE a.employee_no = 13844 and a.advices_date between '1.10.2014' and '31.10.2014'
GROUP BY a.employee_no, comment
So I need result like this :
Employee_no Comment Total
1 aaa 110.00
I normally work with MySQL here, but I believe what I am going to say also a apply for InterBase.
You could just use a case or a if, if any of those exists in your databaseT, you could use a MIN, MAX or any other group function.
MIN(IF(EA.status = 0, a.comment, NULL))
or
MIN(CASE EA.status WHEN 0 then a.comment ELSE NULL END)
This works because group functions ignore NULL values.
The only thing is that, if there is more then one row with status = 0, you won't really be able to control and get the first or the last (you really can't control the order in which the rows are processed by group function unless the function itself support ORDER BY), unless your database has a group function that does the trick.

Deleting rows from a SQL table based on specific criteria

I have a table that contains the following fields: issues to go (itg), customer number (ctm_nbr) and pub code (pub_cde). The data looks like this
12 010000024412 CTR
6 010000024412 RTF
18 010000002325 CTR
9 010000002325 RTF
3 010000014789 CTR
1 010000014789 RTF
I need to be able to delete all of the records where the RTF pub code and matching customer number is half of the issues to go (itg) in the CTR pub code for that matching customer. That way once I have all the records removed I would only have records like this remaining:
3 010000014789 CTR
1 010000014789 RTF
You might use something like: Delete all records for customer number x where the customer number has issues to go in CTR field that are twice the issues to go in the RTF field.
Delete
from --table--
where ctm_nbr in (select t2.ctm_nbr
from --table-- t2 join --table-- t3
ON (t2.ctm_nbr = t3.ctm_nbr)
where t2.pub_cde="CTR"
and t3.pub_cde="RTF"
and t2.itg = 2*t3.itg
)
The tricky bit is getting both related records at one time:
delete
a1
from
a a1
where (
a1.pub_cde = 'RTF' and
exists (
select 'x'
from a a2
where a2.ctm_nbr = a1.ctm_nbr and
a2.pub_cde = 'CTR' and
a2.itg = 2 * a1.itg
)
) or (
a1.pub_cde = 'CTR' and
exists (
select 'x'
from a a2
where a2.ctm_nbr = a1.ctm_nbr and
a2.pub_cde = 'RTF' and
a2.itg * 2 = a1.itg
)
);
Example SQL Fiddle
You can use conditional aggregation:
delete from tbl
where ctm_nbr in(
select ctm_nbr
from tbl
group by ctm_nbr
having max(case when pub_cde = 'CTR' then cast(itg as decimal) end) /
max(case when pub_cde = 'RTF' then cast(itg as decimal) end) = 2)
Fiddle: http://sqlfiddle.com/#!6/a7efe/1/0
The reason I casted itg as decimal is to avoid the rounding issue that would otherwise occur due to your column being an interger data type (thanks to Laurence for pointing that out).
DELETE t1
FROM a t1
INNER JOIN a t2
ON t1.ctm_nbr = t2.ctm_nbr
WHERE
((t1.pub_cde = 'CTR') AND
(t2.pub_cde = 'RTF') AND
(2*t2.itg = t1.itg))
OR
((t2.pub_cde = 'CTR') AND
(t1.pub_cde = 'RTF') AND
(2*t1.itg = t2.itg))

Sql in sql server with convert

I am trying to use convert in an where clause in the select statement. My query looks like this:
SELECT DISTINCT TOP 10 [SurveyResult].*
,[Ticket].[RefNumber]
FROM [SurveyResult]
LEFT JOIN [Ticket] ON [SurveyResult].[TicketID] = [Ticket].[TicketID]
JOIN [SurveyResponse] AS SurveyResponse1 ON [SurveyResult].[ResultID] = SurveyResponse1.[ResultID]
JOIN [QuestionAnswer] AS QuestionAnswer1 ON SurveyResponse1.[AnswerID] = QuestionAnswer1.[AnswerID]
JOIN [SurveyQuestion] AS SurveyQuestion1 ON QuestionAnswer1.[QuestionID] = SurveyQuestion1.[QuestionID]
WHERE SurveyQuestion1.[SurveyID] = [SurveyResult].[SurveyID]
AND SurveyQuestion1.[QuestionID] = 'C86CB39A-8FE0-4FE8-B38F-17F1BE611016'
AND CONVERT(INT, SurveyResponse1.[Response]) >= 1
AND CONVERT(INT, SurveyResponse1.[Response]) <= 5
The problem is that I get some errors when converting the values to integer in the where statement.
I know I have some rows that don't contain numbers in the Response column but I filter those so without the convert part in the where clause I get only numbers so it works like this:
SELECT TOP 1000 [ResponseID]
,[ResultID]
,[Response]
FROM [WFSupport].[dbo].[SurveyResponse]
JOIN QuestionAnswer ON SurveyResponse.AnswerID = QuestionAnswer.AnswerID
WHERE QuestionAnswer.QuestionID = 'C10BF42E-5D51-46BC-AD89-E57BA80EECFD'
And in the results I get numbers but once I add the convert part in the statement I I get an error that it can't convert some text to numbers.
Either do like Mark says or just have NULL values default to something numerical, this would give you a where statement like:
WHERE SurveyQuestion1.[SurveyID] = [SurveyResult].[SurveyID]
AND SurveyQuestion1.[QuestionID] = 'C86CB39A-8FE0-4FE8-B38F-17F1BE611016'
AND CONVERT(INT, ISNULL(SurveyResponse1.[Response],0)) BETWEEN 1 AND 5
The important part is the ISNULL() function and I also used BETWEEN to avoid duplicate converts.
Try:
SELECT DISTINCT TOP 10
[SurveyResult].*,
[Ticket].[RefNumber]
FROM
[SurveyResult]
LEFT JOIN [Ticket] ON [SurveyResult].[TicketID] = [Ticket].[TicketID]
JOIN [SurveyResponse] AS SurveyResponse1
ON [SurveyResult].[ResultID] = SurveyResponse1.[ResultID]
JOIN [QuestionAnswer] AS QuestionAnswer1
ON SurveyResponse1.[AnswerID] = QuestionAnswer1.[AnswerID]
JOIN [SurveyQuestion] AS SurveyQuestion1
ON QuestionAnswer1.[QuestionID] = SurveyQuestion1.[QuestionID]
where SurveyQuestion1.[SurveyID] = [SurveyResult].[SurveyID]
AND SurveyQuestion1.[QuestionID] = 'C86CB39A-8FE0-4FE8-B38F-17F1BE611016'
AND CASE SurveyQuestion1.[QuestionID]
WHEN 'C86CB39A-8FE0-4FE8-B38F-17F1BE611016'
THEN Convert(int, SurveyResponse1.[Response])
ELSE 0
END BETWEEN 1 AND 5
(The AND SurveyQuestion1.[QuestionID] = 'C86CB39A-8FE0-4FE8-B38F-17F1BE611016' is retained in case the query is using an index on QuestionID - if not, it can be removed, as the same condition is implicit in the subsequent CASE condition.)
Try this one -
SELECT DISTINCT TOP 10 sr.*, t.[RefNumber]
FROM dbo.SurveyResult sr
JOIN dbo.SurveyResponse sr2 ON sr.[ResultID] = sr2.[ResultID]
JOIN dbo.QuestionAnswer sa ON sr2.[AnswerID] = sa.[AnswerID]
JOIN dbo.SurveyQuestion sq ON sa.[QuestionID] = sq.[QuestionID] AND sq.[SurveyID] = sr.[SurveyID]
LEFT JOIN dbo.Ticket t ON sr.[TicketID] = t.[TicketID]
WHERE sq.[QuestionID] = 'C86CB39A-8FE0-4FE8-B38F-17F1BE611016'
AND CAST(ISNULL(sr2.[Response], 0) AS INT) BETWEEN 1 AND 5