Merging two tables with no unique keys without row number - sql

I have two tables
Table A:
Name
----
Andy
Greg
Table B:
Value
-----
1
2
I want to merge these two tables into one:
Table C:
Result
------
Andy 1
Greg 2
Note:-
without changing the order. I cannot use row numbers as I am using Apache Calcite and it doesn't support that right now.
Is this possible?

WITH X AS
(
SELECT * FROM
(
SELECT NAME AS Val1,
(SELECT Count(*) from #TableA a1 WHERE a1.Name < a2.Name) AS myRowNumber1 FROM #TableA a2
)a1
INNER JOIN
(
SELECT Id AS Val2,
(SELECT Count(*) from #TableB a1 WHERE a1.Id < a2.Id) AS myRowNumber2 FROM #TableB a2
)b1
ON a1.myRowNumber1=b1.myRowNumber2
)
SELECT Val1 +' '+ Val2 AS Result FROM X
You can use Count(*) instead of Row_Number()
OutPut:-
Result
---------
Andy 1
Greg 2

Create a new column as an identifier
SELECT *, ROW_NUMBER() OVER(PARTITION BY NAME ORDER BY NAME) AS id
INTO #NAMES
FROM TABLE1
SELECT *, ROW_NUMBER() OVER(PARTITION BY VALUE ORDER BY VALUE) AS id
INTO #VALUES_TABLE
FROM TABLE2
And then join by the row number that will be called id
SELECT *
FROM #NAMES t1
LEFT JOIN #VALUES_TABLE t2
ON t1.id = t2.id

Calcite does not have a function exactly like Oracle's ROWNUM pseudocolumn but it does have the standard SQL window function ROW_NUMBER(). You can use it as follows:
create table a as select * from (values ('Andy'), ('Greg')) as t (name);
create table b as select * from (values (1), (2)) as t (v);
select *
from (select name, row_number() over () as id from a)
join (select v, row_number() over () as id from b)
using (id);
+----+------+---+
| ID | NAME | V |
+----+------+---+
| 1 | Andy | 1 |
| 2 | Greg | 2 |
+----+------+---+
(2 rows)
If you want deterministic order, you can change over () to, say, over (order by name desc).

Related

Select item that is different in each of group by a column

I have this sample table
+--------+-------------+
| DBName | Description |
+--------+-------------+
| A | Car |
| A | Boat |
| B | Car |
| B | Plane |
| C | Car |
| C | Boat |
| C | Plane |
+--------+-------------+
I want to take only Description that is not exist on every DBName and show what DBName that don't have the Description.
The Result from the query that I want
+--------+-------------+
| DBName | Description |
+--------+-------------+
| A | Plane |
| B | Boat |
+--------+-------------+
Keep in mind it will be more than just A,B,C on DBName.
Interesting issue. Here are a couple of options for solving. There's discussions around these techniques here, along with a few suggestions of other routes for handling scenarios such as this.
SQL Fiddle Example
select DBName, Description
from (
select DBName, Description
from (select distinct DBName from demo) a
cross join (select distinct Description from demo) b
) c
except
select DbName, Description from demo
This solution works by fetching every possible combination (via cross join of distinct values for each column), then excluding all those which already exist via the except clause.
SQL Fiddle Example
select [each].DBName, missing.Description
from (select distinct DBName from demo) [each]
cross join (select distinct Description from demo) [missing]
where not exists
(
select top 1 1
from demo [exists]
where [exists].DbName = [each].DBName
and [exists].Description = [missing].Description
)
This solution is the same as the above, only instead of the except cluase we use where not exists to remove existing combos.
Ideally you should have a master list of data. In case you do not you should deriv3 it from the data and then put checks against them like below:
SQL Fiddle Example
select
masterlistDbname.Dbname,
masterlistDesc.Description
from
(
select distinct Description from yourtable
) masterlistDesc
cross join
(
select distinct Dbname from yourtable
) masterlistDbname
left join
yourtable t1
on t1.Dbname = masterlistDbname.Dbname
and t1.Description = masterlistDesc.Description
where t1.Dbname is NULL
use NOT EXISTS
SELECT *
FROM yourtable t
WHERE NOT EXISTS
(
SELECT *
FROM yourtable x
WHERE x.Description = t.Description
AND x.DBName <> t.DBName
)
you should throw little more Sample data.
Try this,
create table #test(DBName varchar(50),Descriptions varchar(50) )
insert into #test VALUES
('A','Car')
,('A','Boat')
,('B','Car')
,('B','Plane')
,('C','Car')
,('C','Boat')
,('C','Plane')
;
WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
ORDER BY (
SELECT NULL
)
) rn
,ROW_NUMBER() OVER (
PARTITION BY DBName ORDER BY (
SELECT NULL
)
) rn1
FROM #test
)
SELECT t.DBName
,t1.Descriptions
FROM cte t
CROSS APPLY (
SELECT TOP 1 Descriptions
FROM cte t1
WHERE t1.rn > t.rn
AND t.Descriptions <> t1.Descriptions
AND t.dbname <> t1.dbname
ORDER BY t1.rn
) t1
WHERE t.rn1 = 1
drop table #test

Combine between parallel rows only, in SQL

Given two tables with different rows and values,how can I combine this two tables so the first row of the first table will be combined in a new row with the first row of the second table and so on.. The requirement is that unlike Cartesian product, only the parallel rows will be combined together on the new table.
I am working with PL/SQL Developer
For example(the values are just an example as well):
Table 1:
LongId
100
200
300
Table 2:
Id
1
2
3
The new table:
Id LongId
1 100
2 200
3 300
Use:
SELECT id, longid
FROM (
SELECT t.*,
row_number() over( order by LONGID ) as XXX
FROM table1 t
)
JOIN (
SELECT t.*,
row_number() over( order by id) as XXX
FROM table2 t
)
USING ( xxx )
Demo: http://sqlfiddle.com/#!4/ba9b1/5
| ID | LONGID |
|----|--------|
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
You can use row_number() on both tables and then use that to join on.
SELECT t2.id, t1.longid
FROM
(SELECT table1.*, ROW_NUMBER() OVER (ORDER BY longid) AS rn
FROM table1) t1
JOIN
(SELECT table2.*, ROW_NUMBER() OVER (ORDER BY id) AS rn
FROM table2) t2 on t1.rn = t2.rn

Two rows with the same id and two different values, getting the second value into another column

I have two rows with the same id but different values. I want a query to get the second value and display it in the first row.
There are only two rows for each productId and 2 different values.
I've tried looking for this for the solution everywhere.
What I have, example:
+-----+-------+
| ID | Value |
+-----+-------+
| 123 | 1 |
| 123 | 2 |
+-----+-------+
What I want
+------+-------+---------+
| ID | Value | Value 1 |
+------+-------+---------+
| 123 | 1 | 2 |
+------+-------+---------+
Not sure whether order matters to you. Here is one way:
SELECT MIN(Value), MAX(Value), ID
FROM Table
GROUP BY ID;
This is a self-join:
SELECT a.ID, a.Value, b.Value
FROM table a
JOIN table b on a.ID = b.ID
and a.Value <> b.Value
You can use a LEFT JOIN instead if there are IDs that only have one value and would be lost by the above JOIN
May be you may try this
DECLARE #T TABLE
(
Id INT,
Val INT
)
INSERT INTO #T
VALUES(123,1),(123,2),
(456,1),(789,1),(789,2)
;WITH CTE
AS
(
SELECT
RN = ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Val),
*
FROM #T
)
SELECT
*
FROM CTE
PIVOT
(
MAX(Val)
FOR
RN IN
(
[1],[2]--Add More Numbers here if there are more values
)
)Q

T-SQL - Get a list of all As which have the same set of Bs

I'm struggling with a tricky SQL query that I'm trying to write. Have a look at the following table:
+---+---+
| A | B |
+---+---+
| 1 | 2 |
| 1 | 3 |
| 2 | 2 |
| 2 | 3 |
| 2 | 4 |
| 3 | 2 |
| 3 | 3 |
| 4 | 2 |
| 4 | 3 |
| 4 | 4 |
+---+---+
Now, from this table, I essentially want a list of all As which have the exact same set of Bs and give each set an incrementing ID.
Hence, the output set for the above would be:
+---+----+
| A | ID |
+---+----+
| 1 | 1 |
| 3 | 1 |
| 2 | 2 |
| 4 | 2 |
+---+----+
Thanks.
Edit: If it helps, I have a list of all distinct values of B that are possible in another table.
Edit: Thank you so much for all the innovative answers. Was able to learn a lot indeed.
Here is mathematical trick to solve your tricky select:
with pow as(select *, b * power(10, row_number()
over(partition by a order by b)) as rn from t)
select a, dense_rank() over( order by sum(rn)) as rn
from pow
group by a
order by rn, a
Fiddle http://sqlfiddle.com/#!3/6b98d/11
This of course will work only for limited distinct count as you will get overflow. Here is more general solution with strings:
select a,
dense_rank() over(order by (select '.' + cast(b as varchar(max))
from t t2 where t1.a = t2.a
order by b
for xml path(''))) rn
from t t1
group by a
order by rn, a
Fiddle http://sqlfiddle.com/#!3/6b98d/29
Something like this:
select a, dense_rank() over (order by g) as id_b
from (
select a,
(select b from MyTable s where s.a=a.a order by b FOR XML PATH('')) g
from MyTable a
group by a
) a
order by id_b,a
Or maybe using a CTE (I avoid them when possible)
Sql Fiddle
As a side note, this is the output of the inner query using the sample data in the question:
a g
1 <b>2</b><b>3</b>
2 <b>2</b><b>3</b><b>4</b>
3 <b>2</b><b>3</b>
4 <b>2</b><b>3</b><b>4</b>
Here's a long winded approach, by finding sets with the same elements (using EXCEPT bidirectionally to eliminate, and just done a half diagonal cartesian product), then pairing equal sets up, stamping each pair with a ROW_NUMBER(), before unpivoting the pairs of A's into to your final output where the equivalent sets are projected as rows which have the same id.
WITH joinedSets AS
(
SELECT t1.A as t1A, t2.A AS t2A
FROM MyTable t1
INNER JOIN MyTable t2
ON t1.B = t2.B
AND t1.A < t2.A
),
equalSets AS
(
SELECT js.t1A, js.t2A, ROW_NUMBER() OVER (ORDER BY js.t1A) AS Id
FROM joinedSets js
GROUP BY js.t1A, js.t2A
HAVING NOT EXISTS ((SELECT mt.B FROM MyTable mt WHERE mt.A = js.t1A)
EXCEPT (SELECT mt.B FROM MyTable mt WHERE mt.A = js.t2A))
AND NOT EXISTS ((SELECT mt.B FROM MyTable mt WHERE mt.A = js.t2A)
EXCEPT (SELECT mt.B FROM MyTable mt WHERE mt.A = js.t1A))
)
SELECT A, Id
FROM equalSets
UNPIVOT
(
A
FOR ACol in (t1A, t2A)
) unp;
SqlFiddle here
As it stands, this solution will only work with pairs of sets, not triples etc. A general NTuple type solution is probably possible (but beyond my brain right now).
Here is a very simple, fast, but approximate solution.
It is possible that CHECKSUM_AGG returns the same checksum for different sets of B.
DECLARE #T TABLE (A int, B int);
INSERT INTO #T VALUES
(1, 2),(1, 3),(2, 2),(2, 3),(2, 4),(3, 2),(3, 3),(4, 2),(4, 3),(4, 4);
SELECT
A
,CHECKSUM_AGG(B) AS CheckSumB
,ROW_NUMBER() OVER (PARTITION BY CHECKSUM_AGG(B) ORDER BY A) AS GroupNumber
FROM #T
GROUP BY A
ORDER BY A, GroupNumber;
Result set
A CheckSumB GroupNumber
-----------------------------
1 1 1
2 5 1
3 1 2
4 5 2
For exact solution group by A and concatenate all B values into a long (binary) string using either FOR XML, CLR, or T-SQL function. Then you can partition ROW_NUMBER by that concatenated string to assign numbers to the groups. As shown in other answers.
EDIT
I am changing the code, but it will get bigger now, took help from
Concatenate many rows into a single text string? for concatinating strings
Select [A],
Left(M.[C],Len(M.[C])-1) As [D] into #tempSomeTable
From
(
Select distinct T2.[A],
(
Select Cast(T1.[B] as VARCHAR) + ',' AS [text()]
From sometable T1
Where T1.[A] = T2.[A]
ORDER BY T1.[A]
For XML PATH ('')
) [C]
From sometable T2
)M
SELECT t.A, DENSE_RANK() OVER(ORDER BY t.[D]) [ID] FROM
#tempSomeTable t
inner join
(SELECT [D] FROM(
SELECT [D], COUNT([A]) [D_A] from
#tempSomeTable t
GROUP BY [D] )P where [C_A]>1)t1 on t1.[D]=t.[D]
Here is an exact, rather than approximate, solution. It uses nothing more advanced than INNER JOIN and GROUP BY (and, of course, the DENSE_RANK() to get the ID you want).
It is also general, in that it allows for B values to be repeated within an A group.
SELECT A,
DENSE_RANK() OVER (ORDER BY MIN_EQUIVALENT_A) AS ID
FROM (
SELECT MATCHES.A1 AS A,
MIN(MATCHES.A2) AS MIN_EQUIVALENT_A
FROM (
SELECT T1.A AS A1,
T2.A AS A2,
COUNT(*) AS NUM_B_VALS_MATCHED
FROM (
SELECT A,
B,
COUNT(*) AS B_VAL_FREQ
FROM MyTable
GROUP BY A,
B
) AS T1
INNER JOIN
(
SELECT A,
B,
COUNT(*) AS B_VAL_FREQ
FROM MyTable
GROUP BY A,
B
) AS T2
ON T1.B = T2.B
AND T1.B_VAL_FREQ = T2.B_VAL_FREQ
GROUP BY T1.A,
T2.A
) AS MATCHES
INNER JOIN
(
SELECT A,
COUNT(DISTINCT B) AS NUM_B_VALS_TOTAL
FROM MyTable
GROUP BY A
) AS CHECK_TOTALS_A1
ON MATCHES.A1 = CHECK_TOTALS_A1.A
AND MATCHES.NUM_B_VALS_MATCHED
= CHECK_TOTALS_A1.NUM_B_VALS_TOTAL
INNER JOIN
(
SELECT A,
COUNT(DISTINCT B) AS NUM_B_VALS_TOTAL
FROM MyTable
GROUP BY A
) AS CHECK_TOTALS_A2
ON MATCHES.A2 = CHECK_TOTALS_A2.A
AND MATCHES.NUM_B_VALS_MATCHED
= CHECK_TOTALS_A2.NUM_B_VALS_TOTAL
GROUP BY MATCHES.A1
) AS EQUIVALENCE_TABLE
ORDER BY 2,1
;

Select only one row with same key value from a table

I have this table:
reg_no | cname | no
1 | X | 1
1 | Y | 2
2 | X | 1
2 | Y | 2
What I want to do is to select all that rows but I only want one row for each reg_no when I arrange it in desc (it should only get the row with highest no for each reg_no).
The output should be:
1 Y 2
2 Y 2
Use Row_Number() window function
select Reg_no,C_name,no from
(
select row_number() over(partition by reg_no order by no desc) Rn,*
from yourtable
) A
where rn=1
or ANSI SQL standard will work in sql server 2000. Find the max no in each reg_no then join the result back to the main table.
select A.Reg_no,A.C_name,A.no
from yourtable As A
Inner Join
(
select max(no) As no,Reg_no
from yourtable
group by Reg_No
) As B
on A.No=B.No and A.Reg_No=B.Reg_no
In MSSQL using CROSS APPLY this would be
SELECT DISTINCT
r1.reg_no, r2.cname, r2.no
FROM
table_name r1
CROSS APPLY
(SELECT TOP 1
r.cname, r.no
FROM
table_name r
WHERE r1.reg_no = r.reg_no
ORDER BY r.no DESC) r2