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

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

Related

Find duplicates in SQL Server database where one of the columns must differ

I'm trying to write a SQL query to find duplicates. What I can't manage to do is to make my query only select duplicates where one of the columns value must differ. So, I want to find all the duplicates where all the columns are the same, but one of the values must differ.
What I've got at the moment:
SELECT
a.1, underlag.1, f.1, f.2, f.3, f.4, f.5, f.6, f.7, f.8,
COUNT(*) TotalCount
FROM
f
JOIN
a ON a.Id = f.Id
JOIN
underlag ON underlag.Id = f.Id
GROUP BY
a.1, underlag.1, f.1, f.2, f.3, f.4, f.5, f.6, f.7, f.8
HAVING
COUNT(*) > 1
ORDER BY
underlag.1
The column that I want to differ is f.9 but I've no clue on how to do this. Any help or pointers in the right direction would be great!
SELECT *
FROM (
SELECT
a1 = a.[1]
, underlag1 = underlag.[1]
, f.[1], f.[2], f.[3], f.[4], f.[5], f.[6], f.[7], f.[8], f.[9]
, val = SUM(1) OVER (PARTITION BY CHECKSUM(f.[1], f.[2], f.[3], f.[4], f.[5], f.[6], f.[7], f.[8]))
FROM f
JOIN a on a.Id = f.Id
JOIN underlag on underlag.Id = f.Id
) t
WHERE t.val > 1
ORDER BY underlag1

Oracle SQL - using the coalesce function

I really can't get my head around the coalesce function ... or if this is even the best way to get the result I'm trying to achieve.
I have three dates in the following script (iv.dated, iv1.dated, dh.actshpdate). When I run the following script the dates are in separate columns (as expected);
select unique li.catnr, li.av_part_no, li.artist||' / '||li.title description, li.cust_catnr pallet_ref,
trunc(iv.dated), trunc(iv1.dated), trunc(dh.actshpdate)
from leos_item li
left join invtran_view_oes iv
on li.av_part_no = iv.part_no
and (iv.transaction = 'NREC' and iv.location_no = ' RETURNS W')
left join invtran_view_oes iv1
on li.av_part_no = iv1.part_no
and (iv1.transaction = 'CORR+' and iv1.remark like 'STOCK FROM SP PALLET%')
left join oes_delsegview od
on od.catnr = li.catnr
and od.prodtyp = li.prodtyp
and od.packtyp = li.packtyp
left join oes_dpos dp
on od.ordnr = dp.ordnr
and od.posnr = dp.posnr
and od.segnr = dp.segnr
left join oes_dhead dh
on dp.dheadnr = dh.dheadnr
where li.cunr = '816900'
and substr(li.catnr,1,5) in ('RGMCD','RGJCD')
and li.item_type = 'FP'
and li.catnr = 'RGJCD221'
What I would like to achieve is one column with all dates in date order.
I tried replacing my dates with ...
trunc(coalesce(iv.dated, iv1.dated, dh.actshpdate)) transaction_date
... but, I lose some of the dates;
How can I achieve the following result?
You could use UNION in the following way -
WITH DATA AS(
<your query goes here>
)
SELECT A, b, c, d, e FROM DATA
UNION
SELECT A,b,c,d,f FROM DATA
UNION
SELECT A,b,c,d,g FROM DATA
where a, b, c, d, e, f, g are the column alias of the select list in your original query. You can give your own column alias in the UNION query.

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

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

MySQL: Using REPLACE as part of an IN statement

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.