SQL Select Between Two tables - sql

I have these two tables:
T1:
ref || Name
===========
1 || A
2 || B
3 || C
4 || D
5 || E
And
T2:
ref || Name
===========
1 || w
2 || x
6 || y
7 || z
I need this result:
Name1 || Name2
==============
A || w
B || x
C || y
D || z
E || NULL
I mean some kind of full outer join, on column ref, that will not produce NULL value until there is not any record.
The priority of join is with the values that have same ref, and if row count of tables are not equal, there are some NULL results

Use UPD: here is how you may combine values by values count from different tables:
with t1_values as (
SELECT
name,
row_number() over (order by ref) as position
FROM #t1
),
t2_values as (
SELECT
name,
row_number() over (order by ref) as position
FROM #t2
)
SELECT
t1_values.name as name1,
t2_values.name as name2
FROM t1_values
left JOIN t2_values on t1_values.position = t2_values.position

This is very complicated. You want rows that match to match. Then you want unmatched rows to match unmatched rows by position, and then everything else.
with matches as (
select distinct t1.ref
from t1
where exists (select 1 from t2 where t2.ref = t1.ref)
),
tt1 as (
select t1.*, m.ref as match_ref,
row_number() over (partition by m.ref order by t1.ref) as alt_ref
from t1 left join
matches m
on t1.ref = m.ref
),
tt2 as (
select t2.*, m.ref as match_ref,
row_number() over (partition by m.ref order by t2.ref) as alt_ref
from t2 left join
matches m
on t2.ref = m.ref
)
select tt1.name, tt2.name
from tt1 left join
tt2
on tt1.match_ref = tt2.match_ref or
(tt1.match_ref is null and tt2.match_ref is null and tt1.alt_ref = tt2.alt_ref);
Here is the idea. For each row in both tables, add two new columns:
match_ref is ref when ref exists in the other table.
alt_ref is an enumerated column for the ref values that do not match.
Once you have these columns, it is possible to join the tables together, by first checking match_ref and then -- if that is not present -- checking alt.ref.
SQL Fiddle does not appear to be working for SQL Server. However, here is an identical Postgres version that does work. Here is a working version using SQL Server (this is identical to the Postgres version).

A look at your Question
Correct me if I am wrong, but you have two tables that have an reference ID column of names that you wish to return results sets....only, you do not say distinct so you might end up with extras.
...some kind of full outer join, on column ref, that will not
produce NULL value until there is not any record..
The priority of the JOIN is with the values that have same ref, and if row count of tables are
not equal, there are some NULL results
Actually, the result is not really deterministic. Anyways, this question is still too vague. However, I think you really just want rows matching columns to appear together and anything not....well, not. But return everything.
So, if you know which side is larger, try this:
WITH C AS (SELECT DENSE_RANK() OVER (PARTITION BY ref ORDER BY NAME DESC) AS ROW_ID
, ref
, Name AS Name1
FROM T1)
SELECT Name1, B.Name2
FROM C
LEFT OUTER JOIN (SELECT DENSE_RANK() (OVER PARTITION BY ref ORDER BY NAME DESC) AS ROW_ID
, ref
, Name AS Name2) B ON B.Row_ID = C.Row_ID AND B.ref = C.ref
Each of the ref columns have a distinct ID to attach with. It is done in consecutive order, so if there is still an issue, well, you can figure that logic out. But I'm sure this will help you tremendously get where you are wanting. :)

Thank you every body and excuse me if I speak badly sometimes, count it on my fatigue because of hours working.
I think I couldn't say my meaning correctly, so I answered my own question. this is not the best answer and it is not optimized, I know, but it works!
SELECT t.ref,
t.NAME AS n1,
t2.NAME AS n2
INTO #tbl1
FROM #T1 AS t
LEFT JOIN #T2 AS t2
ON t2.ref = t.ref
WHERE t2.ref IS NOT NULL
SELECT ROW_NUMBER() OVER(ORDER BY t.NAME) AS rn,
*
INTO #tbl2
FROM #T1 AS t
WHERE t.ref NOT IN (SELECT ref
FROM #tbl1)
SELECT ROW_NUMBER() OVER(ORDER BY t.NAME) AS rn,
*
INTO #tbl3
FROM #T2 AS t
WHERE t.ref NOT IN (SELECT ref
FROM #tbl1)
SELECT n1,
n2
FROM #tbl1
UNION
SELECT t1.NAME AS n1,
t2.NAME AS n2
FROM #tbl2 t1
FULL OUTER JOIN #tbl3 t2
ON t1.rn = t2.rn

Related

How can I display two columns together in SQL?

I have 2 queries that return data in the form:
query 1:
column 1
a
b
c
query 2:
column 2
d
e
How can I combine the 2 queries to get output as:
column 1 column 2
a d
b e
c
The order of data in the columns does not matter.
Possibly anything with joins ?
Thanks
use row_number()
select t1.col1,t2.col2 from
(
select *,row_number() over(order by col1) rn from query1
) t1 full outer join
(
select *,row_number() over(order by col2) rn from query2
) t2 on t1.rn=t2.rn
For n,m rows use full outer join
A possible solution is selecting both columns with row_number() and join them by the row_number. One must be aware to select first from the table with the higher number of rows. Example:
select
col_1,
col_2
from (
select
a.col_1,
row_number() over () rn
from a
) s1
FULL OUTER JOIN (
select
b.col_2,
row_number() over () rn
from b
) s2 on s1.rn = s2.rn

Set contains other set in SQL

Is anybody to know how to find sets which contain all sets of other sets
table
id elem
1 A
1 C
2 B
2 D
2 A
2 C
3 A
3 E
3 F
4 F
4 F
I want to get this:
id
2
3
In that case id 2 contains (A,C) - all set of id 1
and id 3 contains (F) - all set of id 4
In my query I need to get all id which contains all set (all elements) at least one id.
I will be very grateful. Thank you.
I didn't have much to go off of for this one, but it was interested so I guessed. I'm using SQL Server
Follow along in Rextester
I submit the answer which has nested selects, then I follow through on the step by step logic to clarify what's going on:
Build a quick table
CREATE TABLE a (id int, t varchar(10))
INSERT INTO a (id, t)
VALUES (1,'A'),(1,'B'),(1,'B'),(1,'B'),(2,'A')
,(2,'B'),(2,'C'),(3,'C'),(3,'D'),(4,'A'),(5,'P');
Here is the solution:
select distinct p_id supersets from
(
select e.p_id, e.c_id, count(*) matches from (
select distinct c.id p_id, c.t p_t, d.id c_id, d.t c_t from a c
inner join a d on c.t = d.t and c.id <> d.id) e
group by e.p_id, e.c_id) sup
inner join
(select id, count(distinct t) reqs from a group by id) sub
on sub.id = sup.c_id and sup.matches = sub.reqs;
Here are the logical steps broken out to help explain why I'm doing what I'm doing:
--Step1
--Create a list of matches between (distinct) values where IDs are not the same
select distinct c.id p_id, c.t p_t, d.id c_id, d.t c_t from a c
inner join a d on c.t = d.t and c.id <> d.id;
--Step2
--Create a unique list of parent IDs and their child IDs and the # of distinct matches
--For example 2 has 2 matches with 1 and vice versa
select e.p_id, e.c_id, count(*) matches from (
select distinct c.id p_id, c.t p_t, d.id c_id, d.t c_t from a c
inner join a d on c.t = d.t and c.id <> d.id) e
group by e.p_id, e.c_id;
--Step2a
--Create a sub query to see how many distinct values are in each "Set"
select id, count(distinct t) reqs from a group by id;
Now we put it all together in the join (above, first) to make sure the total # of matches from Parent to Child make up 100% of the child values ie(is a super set)
I think the following does what you want:
select t1.id
from t t1 join
(select t.*, count(*) over (partition by id) as cnt
from t
) t2
on t1.elem = t2.elem and t1.id <> t2.id
group by t1.id, t2.id, t2.cnt
having count(*) = cnt;
This matches each id to each other id based on the elements. If the number of matches equals the count in the second set, then all match -- and you have a superset.
I notice that you have duplicate elements. Let's handle this with a CTE:
with t as (
select distinct id, elem
from t
)
select t1.id
from t t1 join
(select t.*, count(*) over (partition by id) as cnt
from t
) t2
on t1.elem = t2.elem and t1.id <> t2.id
group by t1.id, t2.id, t2.cnt
having count(*) = cnt;

Listagg function is not working in sub query

Listagg function is not working when it used in subquery, though using listagg function in prod column is not concatenated all the products in single row
select
a.id,
a.num,
(listagg(c.prod_name,',') within group(order by prod_name)
from product c
where c.prod_id = NVL(b.prod_id,b.prod_pos) As prod
from master a, base_product b
where
b.id = a.id and
b.type = 1 and
a.id = 12345;
Your subquery in the select clause is lacking a select keyword, this is probably the immediate source of the error. However, we can improve upon your query and instead join to a subquery which does the aggregation:
select
a.id,
a.num,
coalesce(c.prod, 'NA') as prod
from master a
inner join base_product b
on b.id = a.id
left join
(
select listagg(prod_name, ',') within group(order by prod_name) as prod
from product
) c
on c.prod_id = NVL(b.prod_id, b.prod_pos)
where
b.type = 1 and
a.id = 12345;
Besides refactorting the call to listagg, I also replaced your implicit join syntax with an explicit inner join. This is the preferred way of writing the query, and this style because part of the ANSI SQL standard 25 years ago.
You can use listagg(prod, ',') within group(ORDER BY prod) with group by on column num in cte or derived table and join it to the main table to get id as below.
SELECT t2.id,
t2.num,
t1.prod
FROM
(SELECT num,
listagg(prod, ',') within group(
ORDER BY prod) AS prod
FROM table1
GROUP BY num ) t1
JOIN table1 t2 ON t1.num = t2.num
order by t2.id\\
OR
WITH t1 AS
(SELECT num,
listagg(prod, ',') within group(
ORDER BY prod) AS prod
FROM table1
GROUP BY num)
SELECT t2.id,
t2.num,
t1.prod
FROM t1
JOIN table1 t2 ON t1.num = t2.num
ORDER BY t2.id\\
Given you sample data:
ID NUM PROD
--------------------
101 1701A001 book
102 1701A001 data
103 1702B005 bat
104 1702B005 ball
105 1703C006 Stumps
Result:
ID NUM PROD
-------------------------
101 1701A001 book,data
102 1701A001 book,data
103 1702B005 ball,bat
104 1702B005 ball,bat
105 1703C006 Stumps
DEMO

Querying two tables to filter data using select case

I have two tables
Table 1 looks like this
ID Repeats
-----------
A 1
A 1
A 0
B 2
B 2
C 2
D 1
Table 2 looks like this
ID values
-----------
A 100
B 200
C 100
D 300
Using a view I need a result like this
ID values Repeats
-------------------
A 100 NA
B 200 2
C 100 2
D 300 1
that means, I want unique ID, its values and Repeats. Repeats value should display NA when there are multiple values against single ID and it should display the Repeats value in case there is single value for repeats.
Initially I needed to display the max value of repeats so I tried the following view
ALTER VIEW [dbo].[BookingView1]
AS
SELECT bv.*, bd2.Repeats FROM Table1 bv
JOIN
(
SELECT distinct bd.id, bd.Repeats FROM table2 bd
JOIN
(
SELECT Id, MAX(Repeats) AS MaxRepeatCount
FROM table2
GROUP BY Id
) bd1
ON bd.Id = bd1.Id
AND bd.Repeats = bd1.MaxRepeatCount
) bd2
ON bv.Id = bd2.Id;
and this returns the correct result but when trying to implement the CASE it fails to return unique ID results. Please help!!
One method uses outer apply:
select t2.*, t1.repeats
from table2 t2 outer apply
(select (case when max(repeats) = min(repeats) then max(repeats)
else 'NA'
end) as repeats
from table1 t1
where t1.id = t2.id
) t1;
Two notes:
This assumes that repeats is a string. If it is a number, you need to cast it to a string.
repeats is not null.
For the sake of completeness, I'm including another approach that will work if repeats is NULL. However, Gordon's answer has a much simpler query plan and should be preferred.
Option 1 (Works with NULLs):
SELECT
t1.ID, t2.[Values],
CASE
WHEN COUNT(*) > 1 THEN 'NA'
ELSE CAST(MAX(Repeats) AS VARCHAR(2))
END Repeats
FROM (
SELECT DISTINCT t1.ID, t1.Repeats
FROM #table1 t1
) t1
LEFT OUTER JOIN #table2 t2
ON t1.ID = t2.ID
GROUP BY t1.ID, t2.[Values]
Option 2 (does not contain explicit subqueries, but does not work with NULLs):
SELECT DISTINCT
t1.ID,
t2.[Values],
CASE
WHEN COUNT(t1.Repeats) OVER (PARTITION BY COUNT(DISTINCT t1.Repeats), t1.ID) > 1 THEN 'NA'
ELSE CAST(t1.Repeats AS VARCHAR(2))
END Repeats
FROM #table1 t1
LEFT OUTER JOIN #table2 t2
ON t1.ID = t2.ID
GROUP BY t1.ID, t2.[Values], t1.Repeats
NOTE:
This may not give desired results if table2 has different values for the same ID.

sql to combine two unrelated tables into one

I have tables
table1
col1 col2
a b
c d
and table2
mycol1 mycol2
e f
g h
i j
k l
I want to combine the two tables, which have no common field into one table looking like:
table 3
col1 col2 mycol1 mycol2
a b e f
c d g h
null null i j
null null k l
ie, it is like putting the two tables side by side.
I'm stuck! Please help!
Get a row number for each row in each table, then do a full join using those row numbers:
WITH CTE1 AS
(
SELECT ROW_NUMBER() OVER(ORDER BY col1) AS ROWNUM, * FROM Table1
),
CTE2 AS
(
SELECT ROW_NUMBER() OVER (ORDER BY mycol1) AS ROWNUM, * FROM Table2
)
SELECT col1, col2, mycol1, mycol2
FROM CTE1 FULL JOIN CTE2 ON CTE1.ROWNUM = CTE2.ROWNUM
This is assuming SQL Server >= 2005.
It's really good if you put in a description of why this problem needs to be solved. I'm guessing it is just to practice sql syntax?
Anyway, since the rows don't have anything connecting them, we have to create a connection. I chose the ordering of their values. Also since they have nothing connecting them that also begs the question on why you would want to put them next to each other in the first place.
Here is the complete solution: http://sqlfiddle.com/#!6/67e4c/1
The select code looks like this:
WITH rankedt1 AS
(
SELECT col1
,col2
,row_number() OVER (order by col1,col2) AS rn1
FROM table1
)
,rankedt2 AS
(
SELECT mycol1
,mycol2
,row_number() OVER (order by mycol1,mycol2) AS rn2
FROM table2
)
SELECT
col1,col2,mycol1,mycol2
FROM rankedt1
FULL OUTER JOIN rankedt2
ON rn1=rn2
Option 1: Single Query
You have to join the two tables, and if you want each row in table1 to match to only one row in table2, you have to restrict the join somehow. Calculate row numbers in each table and join on that column. Row numbers are database-specific; here is a solution for mysql:
SELECT
t1.col1, t1.col2, t2.mycol1, t2.mycol2
FROM
(SELECT col1, col2, #t1_row := t1_row + 1 AS rownum FROM table1, (SELECT #t1_row := 0) AS r1) AS t1
LEFT JOIN
(SELECT mycol1, mycol2, #t2_row := t2_row + 1 AS rownum FROM table2, (SELECT #t2_row := 0) AS r2) AS t2
ON t1.rownum = t2.rownum;
This assumes table1 is longer than table2; if table2 is longer, either use RIGHT JOIN or switch the order of the t1 and t2 sub-selects. Also note that you can specify the order of each table separately using an ORDER BY clause in the sub-selects.
(See select increment counter in mysql)
Option 2: Post-processing
Consider making two selects, and then concatenating the results with your favorite scripting language. This is a much more reasonable approach.