SQL SELECT DISTINCT CONCAT(ColumnA,'|',ColumnB) - sql

So I have two tables and I want to use a concat on one of the columns in each table.
TableA
ColumnA
1
1
2
3
4
4
5
TableB
ColumnX
a
a
b
c
d
d
e
And I want to concat these two columns so they end up looking like the below result WITHOUT dublicates
Result
1|a
2|b
3|c
4|d
5|e
So I have tried to do the following:
SELECT DISTINCT CONCAT(ColumnA,'|',ColumnB) where tableA.Relation = TableB.Relation
But I am still getting dublicates!? Why????

you could generate the row_numbers by row_numbers() function & then concat them (SQL Server)
;with cte as
(
SELECT *, ROW_NUMBER() over (order by (select 1)) rn FROM <table>
),cte1 as
(
SELECT *, ROW_NUMBER() over (order by (select 1)) rn FROM <table>
)
select DISTINCT CONCAT(c.ColumnA, '|',c1.ColumnX) from cte c
join cte1 c1 on c1.rn = c.rn

This is similar only without the CTE
select distinct A.a + '|' + B.b
from
(select a, Row_Number() over (order by a) as rowNum
from TableA) as A,
(select b, Row_Number() over (order by b) as rowNum
from TableB) as B
where A.rowNum = b.rowNum

Related

Random allocation of records between two tables

I want to assign the values from Table B to Table A, but so that each record in Table B occurs in the same number of repetitions.
Fiddle SQL
You can use window functions for this and mod arithmetic. For simple repetition:
with a as (
select a.*, rownum as seqnum
from a
),
b as (
select b.*, rownum as seqnum, count(*) over () as cnt
from b
)
select a.col, b.col
from a join
b
on mod(a.seqnum - 1, b.cnt) = b.seqnum - 1;
For more random assignment, randomize the seqnums:
with a as (
select a.*,
row_number() over (order by dbms_random.random) as seqnum
from a
order by db
),
b as (
select b.*, count(*) over () as cnt,
row_number() over (order by dbms_random.random) as seqnum
from b
)
select a.col, b.col
from a join
b
on mod(a.seqnum - 1, b.cnt) = b.seqnum - 1;
You can use the ROWNUM for achieving the same:
SELECT
COLOUR,
EMP
FROM
(
SELECT
COLOUR,
ROWNUM RN
FROM
T1
) T1,
(
SELECT
EMP,
ROWNUM RN
FROM
T2
) T2
WHERE
MOD(T1.RN, 2) + 1 = T2.RN
Fiddler SQL QUERY
Try this,
with Employees as
(select Emp, Row_Number() Over(order by 1) Rn
from B
cross join (select 1
from Dual
connect by level < (select count(1)
from A) / (select count(1)
from B) + 1)
order by Dbms_Random.Value),
Colours as
(select Colour, Rownum Rn
from A)
select t.Colour, k.Emp
from Colours t
join Employees k
on t.Rn = k.Rn

SQL Self Recursive Join with Grouping

In SQL Server, I have the following table:
Name New_Name
---------------
A B
B C
C D
G H
H I
Z B
I want to create a new table that links all the names that are related to a single new groupID
GroupID Name
-------------
1 A
1 B
1 C
1 D
1 Z
2 G
2 H
2 I
I'm a bit stuck on this can can't figure out how to do it apart from a bunch of joins. But I would like to do it properly.
Edited the question to allow grouping from two different start points, A and Z into one group.
Since you've changed the question, I'm updating the answer. Please note that answer is same in sense of logical structure. All it does differently is that instead of going from G to I in calculating levels, answer now calculates from I to G.
Working demo link
;with cte as
(
select
t1.Name as Name, row_number() over (order by t1.Name) r,
t1.New_Name as New_Name,
1 as level
from yt t1 left join yt t2
on t1.New_Name=t2.name
where t2.name is null
union all
select
yt.Name as Name, r,
yt.New_Name as New_Name,
c.level+1 as level
from cte c join yt
on yt.New_Name=c.Name
),
cte2 as
(
select r as group_id, Name from cte
union
select c1.r as group_id, c1.New_name as Name from cte c1
where level = (select min(level) from cte c2 where c2.r=c1.r)
)
select * from cte2;
Below is old answer.
You can try below CTE based query:
create table yt (Name varchar(10), New_Name varchar(10));
insert into yt values
('A','B'),
('B','C'),
('C','D'),
('G','H'),
('H','I');
;with cte as
(
select
t1.Name as Name, row_number() over (order by t1.Name) r,
t1.New_Name as New_Name,
1 as level
from yt t1 left join yt t2
on t1.Name=t2.New_name
where t2.new_name is null
union all
select
yt.Name as Name, r,
yt.New_Name as New_Name,
c.level+1 as level
from cte c join yt
on yt.Name=c.New_Name
),
cte2 as
(
select r as group_id, Name from cte
union
select c1.r as group_id, c1.New_name as Name from cte c1
where level = (select max(level) from cte c2 where c2.r=c1.r)
)
select * from cte2;
see working demo
Little bit complex but working.
DECLARE #T TABLE (Name VARCHAR(2), New_Name VARCHAR(2))
INSERT INTO #T
VALUES
('A','B'),
('B','C'),
('C','D'),
('G','H'),
('H','I'),
('Z','B')
;WITH CTE AS
(
SELECT * , RN = ROW_NUMBER() OVER(ORDER BY Name) FROM #T
)
,CTE2 AS (SELECT T1.RN, T1.Name Name1, T1.New_Name New_Name1,
X.Name Name2, X.New_Name New_Name2,
FLAG = CASE WHEN X.Name IS NULL THEN 1 ELSE 0 END
FROM CTE T1
OUTER APPLY (SELECT * FROM CTE T2 WHERE T2.RN > T1.RN
AND (T2.Name IN (T1.Name , T1.New_Name)
OR T2.New_Name IN (T1.Name , T1.New_Name)
)) AS X
)
,CTE3 AS (SELECT *,
GroupID = ROW_NUMBER() OVER (ORDER BY RN) -
ROW_NUMBER() OVER (PARTITION BY Flag ORDER BY RN) +1
FROM CTE2
)
SELECT
DISTINCT GroupID, Name
FROM
(SELECT * FROM CTE3 WHERE Name2 IS NOT NULL) SRC
UNPIVOT ( Name FOR COL IN ([Name1], [New_Name1], [Name2], [New_Name2])) UNPVT
Result
GroupID Name
-------------------- ----
1 A
1 B
1 C
1 D
1 Z
2 G
2 H
2 I

concatenate recursive cross join

I need to concatenate the name in a recursive cross join way. I don't know how to do this, I have tried a CTE using WITH RECURSIVE but no success.
I have a table like this:
group_id | name
---------------
13 | A
13 | B
19 | C
19 | D
31 | E
31 | F
31 | G
Desired output:
combinations
------------
ACE
ACF
ACG
ADE
ADF
ADG
BCE
BCF
BCG
BDE
BDF
BDG
Of course, the results should multiply if I add a 4th (or more) group.
Native Postgresql Syntax:
SqlFiddleDemo
WITH RECURSIVE cte1 AS
(
SELECT *, DENSE_RANK() OVER (ORDER BY group_id) AS rn
FROM mytable
),cte2 AS
(
SELECT
CAST(name AS VARCHAR(4000)) AS name,
rn
FROM cte1
WHERE rn = 1
UNION ALL
SELECT
CAST(CONCAT(c2.name,c1.name) AS VARCHAR(4000)) AS name
,c1.rn
FROM cte1 c1
JOIN cte2 c2
ON c1.rn = c2.rn + 1
)
SELECT name as combinations
FROM cte2
WHERE LENGTH(name) = (SELECT MAX(rn) FROM cte1)
ORDER BY name;
Before:
I hope if you don't mind that I use SQL Server Syntax:
Sample:
CREATE TABLE #mytable(
ID INTEGER NOT NULL
,TYPE VARCHAR(MAX) NOT NULL
);
INSERT INTO #mytable(ID,TYPE) VALUES (13,'A');
INSERT INTO #mytable(ID,TYPE) VALUES (13,'B');
INSERT INTO #mytable(ID,TYPE) VALUES (19,'C');
INSERT INTO #mytable(ID,TYPE) VALUES (19,'D');
INSERT INTO #mytable(ID,TYPE) VALUES (31,'E');
INSERT INTO #mytable(ID,TYPE) VALUES (31,'F');
INSERT INTO #mytable(ID,TYPE) VALUES (31,'G');
Main query:
WITH cte1 AS
(
SELECT *, rn = DENSE_RANK() OVER (ORDER BY ID)
FROM #mytable
),cte2 AS
(
SELECT
TYPE = CAST(TYPE AS VARCHAR(MAX)),
rn
FROM cte1
WHERE rn = 1
UNION ALL
SELECT
[Type] = CAST(CONCAT(c2.TYPE,c1.TYPE) AS VARCHAR(MAX))
,c1.rn
FROM cte1 c1
JOIN cte2 c2
ON c1.rn = c2.rn + 1
)
SELECT *
FROM cte2
WHERE LEN(Type) = (SELECT MAX(rn) FROM cte1)
ORDER BY Type;
LiveDemo
I've assumed that the order of "cross join" is dependent on ascending ID.
cte1 generate DENSE_RANK() because your IDs contain gaps
cte2 recursive part with CONCAT
main query just filter out required length and sort string
The recursive query is a bit simpler in Postgres:
WITH RECURSIVE t AS ( -- to produce gapless group numbers
SELECT dense_rank() OVER (ORDER BY group_id) AS grp, name
FROM tbl
)
, cte AS (
SELECT grp, name
FROM t
WHERE grp = 1
UNION ALL
SELECT t.grp, c.name || t.name
FROM cte c
JOIN t ON t.grp = c.grp + 1
)
SELECT name AS combi
FROM cte
WHERE grp = (SELECT max(grp) FROM t)
ORDER BY 1;
The basic logic is the same as in the SQL Server version provided by #lad2025, I added a couple of minor improvements.
Or you can use a simple version if your maximum number of groups is not too big (can't be very big, really, since the result set grows exponentially). For a maximum of 5 groups:
WITH t AS ( -- to produce gapless group numbers
SELECT dense_rank() OVER (ORDER BY group_id) AS grp, name AS n
FROM tbl
)
SELECT concat(t1.n, t2.n, t3.n, t4.n, t5.n) AS combi
FROM (SELECT n FROM t WHERE grp = 1) t1
LEFT JOIN (SELECT n FROM t WHERE grp = 2) t2 ON true
LEFT JOIN (SELECT n FROM t WHERE grp = 3) t3 ON true
LEFT JOIN (SELECT n FROM t WHERE grp = 4) t4 ON true
LEFT JOIN (SELECT n FROM t WHERE grp = 5) t5 ON true
ORDER BY 1;
Probably faster for few groups. LEFT JOIN .. ON true makes this work even if higher levels are missing. concat() ignores NULL values. Test with EXPLAIN ANALYZE to be sure.
SQL Fiddle showing both.

Some need help on SQL statement

Assume the following table:
Select *
from TestTable;
Name value
B 3
C 1
A 2
I want to output as follows:
Name value
A 1
B 2
C 3
Notice that in the result Names are matched to their corresponding ordinal values from the value column.
Anyone help, how can I write the SQL statement?
select
Name, Value
from
-- Order the Name table
(select row_number() over (order by name) as id, Name from TestTable) as n
inner join
-- Order the Value table
(select row_number() over (order by value) as id, Value from TestTable) as v
on n.id = v.id -- Combine two table by the ordered id
SELECT A.Name, B.Value FROM (
SELECT ROW_NUMBER() OVER(ORDER BY Name) as N, Name FROM Tab ) as A
INNER JOIN (SELECT ROW_NUMBER() OVER(ORDER BY Value) as N, Value FROM Tab) as B on B.N = A.N
WITH
test1 AS
(SELECT ROW_NUMBER() over(order by Name)as id , Name FROM Testing ),
test2 AS
(SELECT ROW_NUMBER() over(order by value)as id , value FROM Testing )
SELECT Name,Value
FROM test1
JOIN test2
ON test1.id=test2.id

Order by newly selected column

I have a query like:
SELECT
R.*
FROM
(SELECT A, B,
(SELECT smth from another table) as C,
ROW_NUMBER() OVER (ORDER BY C DESC) AS RowNumber
FROM SomeTable) R
WHERE
RowNumber BETWEEN 10 AND 20
This gives me an error on ORDER BY C DESC.
I understand why this error is caused, so I've thought of adding another SELECT with ORDER BY and only than selecting rows from 10 to 20. But I don't think it's good to have 3 nested SELECT commands.
How else is it possible to select these rows?
A column cannot refer to an alias on same level, you have to table-derive it first, or use CTE.
SELECT
R.* , ROW_NUMBER() OVER (ORDER BY C DESC) AS RowNumber
FROM
(SELECT A, B, (SELECT smth from another table) as C
FROM SomeTable) R
-- WHERE
-- but you still cannot do this
-- RowNumber BETWEEN 10 AND 20
Need to do this:
select S.*
from
(
SELECT
R.* , ROW_NUMBER() OVER (ORDER BY C DESC) AS RowNumber
FROM
(SELECT A, B,
(SELECT smth from another table) as C
FROM SomeTable) R
) as s
where s.RowNumber between 10 and 20
To avoid deep nesting and to make it at least look pleasant, use CTE:
with R as
(
SELECT A, B, (SELECT smth from another table) as C
FROM SomeTable
)
,S AS
(
SELECT R.*, ROW_NUMBER() OVER (ORDER BY C DESC) AS RowNumber
FROM R
)
SELECT S.*
FROM S
WHERE S.RowNumber BETWEEN 1 AND 20
You cannot use aliased columns in the same SELECT, but you can wrap it into another select to make it work:
SELECT R.*
FROM (SELECT ABC.A, ABC.B, ABC.C, ROW_NUMBER() OVER (ORDER BY C DESC) AS RowNumber
FROM (SELECT A, B, (SELECT smth from another table) as C FROM SomeTable) ABC
) R
WHERE R.RowNumber BETWEEN 10 AND 20