i have the following table
A B
mp1 Y3013H301
mp2 924071
mp2 930081
mp3 200130543
mp4 3726474
mp5 15/2014
mp5 16/2014
mp6 BBB032232
mp6 BBB032572
mp6 BBB032574
mp6 BBB0325711
i would like to have the following output , indiferent on the number of A or B's :
A B
mp1 Y3013H301
mp2 924071 ; 930081
mp3 200130543
mp4 3726474
mp5 15/2014 ; 16/2014
mp6 BBB032232 ; BBB032572 ; BBB032574 ; BBB0325711
is there any way to do this?
Thanks in advance!
You can do it using FOR XML PATH('') in the inner query to get all the values of B for each A.
select distinct T1.A,
(select STUFF( (select ' ; ' + T2.B
from mytable T2
where T1.A = T2.A
FOR XML PATH('')
), 1, 3, '')) AS B
from mytable T1
SQL Fiddle Demo
Related
I can't replace every 2 characters of a string with a '.'
select STUFF('abcdefghi', 3, 1, '.') c3,STUFF('abcdefghi', 5, 1,
'.') c5,STUFF('abcdefghi', 7, 1, '.') c7,STUFF('abcdefghi', 9, 1, '.')
c9
if I use STUFF I should subsequently overlap the strings c3, c5, c7 and c9. but I can't find a method
can you help me?
initial string:
abcdefghi
the result I would like is
ab.de.gh.
the string can be up to 50 characters
Create a numbers / tally / digits table, if you don't have one already, then you can use this to target each character position:
with digits as ( /* This would be a real table, here it's just to test */
select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))x(n)
), t as (
select 'abcdefghi' as s
)
select String_Agg( case when d.n%3 = 0 then '.' else Substring(t.s, d.n, 1) end, '')
from t
cross apply digits d
where d.n <Len(t.s)
Using for xml with existing table
with digits as (
select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))x(n)
),
r as (
select t.id, case when d.n%3=0 then '.' else Substring(t.s, d.n, 1) end ch
from t
cross apply digits d
where d.n <Len(t.s)
)
select result=(select '' + ch
from r r2
where r2.id=r.id
for xml path('')
)
from r
group by r.id
You can try it like this:
Easiest might be a quirky update ike here:
DECLARE #string VARCHAR(100)='abcdefghijklmnopqrstuvwxyz';
SELECT #string = STUFF(#string,3*A.pos,1,'.')
FROM (SELECT TOP(LEN(#string)/3) ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM master..spt_values) A(pos);
SELECT #string;
Better/Cleaner/Prettier was a recursive CTE:
We use a declared table to have some tabular sample data
DECLARE #tbl TABLE(ID INT IDENTITY, SomeString VARCHAR(200));
INSERT INTO #tbl VALUES('')
,('a')
,('ab')
,('abc')
,('abcd')
,('abcde')
,('abcdefghijklmnopqrstuvwxyz');
--the query
WITH recCTE AS
(
SELECT ID
,SomeString
,(LEN(SomeString)+1)/3 AS CountDots
,1 AS OccuranceOfDot
,SUBSTRING(SomeString,4,LEN(SomeString)) AS RestString
,CAST(LEFT(SomeString,2) AS VARCHAR(MAX)) AS Growing
FROM #tbl
UNION ALL
SELECT t.ID
,r.SomeString
,r.CountDots
,r.OccuranceOfDot+2
,SUBSTRING(RestString,4,LEN(RestString))
,CONCAT(Growing,'.',LEFT(r.RestString,2))
FROM #tbl t
INNER JOIN recCTE r ON t.ID=r.ID
WHERE r.OccuranceOfDot/2<r.CountDots-1
)
SELECT TOP 1 WITH TIES ID,Growing
FROM recCTE
ORDER BY ROW_NUMBER() OVER(PARTITION BY ID ORDER BY OccuranceOfDot DESC);
--the result
1
2 a
3 ab
4 ab
5 ab
6 ab.de
7 ab.de.gh.jk.mn.pq.st.vw.yz
The idea in short
We use a recursive CTE to walk along the string
we add the needed portion together with a dot
We stop, when the remaining length is to short to continue
a little magic is the ORDER BY ROW_NUMBER() OVER() together with TOP 1 WITH TIES. This will allow all first rows (frist per ID) to appear.
PostgreSQL 11.1
AFAIK, this is correct and should run. It Fails with syntax error on Delete. What am I missing?
Thanks for any help.
ERROR: syntax error at or near "DELETE"
LINE 41: DELETE FROM d
WITH _in (tservice, patient_recid, disease_recid, new_disease_recid) AS (
VALUES ('2021-04-21'::timestamp, '23262'::integer, '34978'::integer, '33364'::integer)
)
UPDATE dx d
SET disease_recid = n.new_disease_recid
FROM _in n,
LATERAL ( WITH RECURSIVE readtoend AS(
SELECT recid, newrecid
FROM patients p1
JOIN _in n ON p1.recid = n.patient_recid
UNION
SELECT c.recid, c.newrecid
FROM patients c
INNER JOIN readtoend s ON s.newrecid = c.recid
),
readtostart AS(
SELECT recid, newrecid
FROM patients p1
JOIN _in n ON p1.recid = n.patient_recid
UNION
SELECT c.recid, c.newrecid
FROM patients c
INNER JOIN readtostart s ON s.recid = c.newrecid
)
SELECT recid FROM readtoend
UNION
SELECT recid FROM readtostart
) j,
LATERAL ( WITH _get_existing_target AS(
SELECT d.*
FROM d
WHERE (d.patient_recid, d.disease_recid) = (j.recid, n.new_disease_recid) AND d.tservice <= n.tservice
),
_get_conflicts AS(
SELECT d.*
FROM d
WHERE (d.patient_recid, d.disease_recid) = (j.recid, n.disease_recid) AND EXISTS ( SELECT 1
FROM _get_existing_target x
WHERE d.patient_recid = x.patient_recid AND d.tservice::date = x.tservice::date)
)
DELETE FROM d
USING _get_conflicts f
WHERE d.recid = f.recid
RETURNING d.*
) del
WHERE (d.patient_recid, d.disease_recid) = (j.recid, n.disease_recid) AND d.tservice::date <= n.tservice::date
AND d.recid NOT IN ( SELECT recid FROM del);
You cannot use DELETE ... RETURNING in the FROM list of a query.
I would like to produce a string containing some parsed numeric ranges.
I have a table with some data
b_id,s_id
1,50
1,51
1,53
1,61
1,62
1,63
2,91
2,95
2,96
2,97
Using only SQL in PostgreSQL, how could I produce this output:
b_id,s_seqs
1,"50-51,53,61-63"
2,"91,95-97"
How on earth do I do that?
select b_id, string_agg(seq, ',' order by seq_no) as s_seqs
from (
select
b_id, seq_no,
replace(regexp_replace(string_agg(s_id::text, ','), ',.+,', '-'), ',', '-') seq
from (
select
b_id, s_id,
sum(mark) over w as seq_no
from (
select
b_id, s_id,
(s_id- 1 <> lag(s_id, 1, s_id) over w)::int as mark
from my_table
window w as (partition by b_id order by s_id)
) s
window w as (partition by b_id order by s_id)
) s
group by 1, 2
) s
group by 1;
Here you can find a step-by-step analyse from the innermost query towards the outside.
I have two tables
TableA has three fields
Id | FieldA | SomethingElse
TableB has three fields as well
Id | FieldA_FK | FieldB
We can join the tables on
TableA.FieldA = TableB.FieldA_FK
I would like to select values on both these tables in order to retrieve the following dataset:
TableA.Id, TableA.FieldA, TableA.SomethingElse, [Concatenation of TableB.FieldB]
To retrieve [Concatenation of TableB.FieldB], I know I can do
declare #result varchar(500);
set #result = '';
select #result = COALESCE(#result + ',', '') + FieldB
from TableB b
join TableA a on a.FieldA = b.FieldA_FK
select #result
How can I get the result described above with the concatenation on one result row only?
Thanks in advance.
Examples of data:
TableA
1 A something
2 B somethingElse
TableB
1 A Aa
2 A Ab
3 A Ac
4 B Ba
5 B Bb
I would like to retrieve
1 A something Aa, Ab, Ac
2 B somethingElse Ba, Bb
You can use the FOR XML PATH command:
SELECT
TableA.Id, TableA.FieldA, TableA.SomethingElse,
[Concatenation of TableB.FieldB] =
(STUFF((SELECT CAST(', ' + TableB.FieldB AS VARCHAR(MAX))
FROM TableB
WHERE (TableA.FieldA = TableB.FieldA_FK)
FOR XML PATH ('')), 1, 2, ''))
FROM TableA
Demo
This isn't as obvious as it should be, but you can abuse SQL's XML methods:
select a.ID, a.FieldA, a.FieldB
, stuff(
(select ', ' + TableB.FieldB
from TableB
inner join TableA on TableB.TableA_FK = TableA.ID
for xml path(''), type
).value('(./text())[1]', 'varchar(max)')
, 1,2,'')
from TableA a
See this fiddle:
http://sqlfiddle.com/#!3/0fdd52/5
I am facing a performance issue due to "Insert into" statement in sql. I am using a CTE to select data from multiple tables and insert into other table. It was working just fine until yesterday. Select takes less than a minute to retrieve the data where as insert into taking forever. Can some one please help me in understanding what i am doing wrong. Any help is highly appreciated. Thanks.
Here is my code:
I am using this query in an SP. I am trying to load 220K records to 1.5M records table.
;with CTE_A
AS
(
SELECT A1, A2,...
FROM dbo.A with (nolock)
WHERE A1 = <some condition>
GROUP BY a.A1,a.A2 , a.A3
), CTE_C as
(
SELECT C1, C2,....
FROM dbo.B with (nolock)
WHERE a.C1 = <some condition>
GROUP BY a.c1,a.C2 , a.C3
)
INSERT INTO [dbo].MainTable
SELECT
A1, A2, A3 , C1, C2, C3
FROM
CTE_A ta with (nolock)
LEFT OUTER JOIN
CTE_C tc with (nolock) ON ta.a1 = tc.a1 and ta.b1 = tc.b1 and ta.c1 = tc.c1
LEFT OUTER JOIN
othertable bs with (nolock) ON usd_bs.c = s.c
AND (A1 BETWEEN bs.a1 AND bs.a1)
AND bs.c1 = 1
try this method (temp table instead cte), perfomance must be much higher for your task
IF OBJECT_ID('Tempdb..#CTE_A') IS NOT NULL
DROP TABLE #CTE_A
IF OBJECT_ID('Tempdb..#CTE_C') IS NOT NULL
DROP TABLE #CTE_C
-------------------------------------------------------------
SELECT A1 ,
A2 ,...
INTO #CTE_A --data set into temp table
FROM dbo.A WITH ( NOLOCK )
WHERE A1 = <some condition>
GROUP BY a.A1 ,
a.A2 ,
a.A3
-------------------------------------------------------------
SELECT C1 ,
C2 ,....
FROM dbo.B WITH ( NOLOCK )
INTO #CTE_C --data set into temp table
WHERE a.C1 = <some condition>
GROUP BY a.c1 ,
a.C2 ,
a.C3
INSERT INTO [dbo].MainTable
SELECT A1 ,
A2 ,
A3 ,
C1 ,
C2 ,
C3
FROM #CTE_A AS ta
LEFT JOIN #CTE_C AS tc ON ta.a1 = tc.a1
AND ta.b1 = tc.b1
AND ta.c1 = tc.c1
LEFT JOIN othertable AS bs ON usd_bs.c = s.c
AND ( A1 BETWEEN bs.a1 AND bs.a1 )
AND bs.c1 = 1