If I have two tables(t1, t2), each with one column
t1
letters
a
b
c
t2
nums
1
2
3
Is it possible to "join" the two together in a way that produces a two-column result set that looks like this:
letters nums
a 1
b 2
c 3
Requirements for the solution:
Must combine each table's data in a specified order, so being able
to order each table's data before joining
Doesn't use any functions, like row_number, to add an extra column to join on
Bonus points:
- If two tables have different row counts, final result set is count of the max of the two tables, and the "missing" data is nulls.
Just wondering if this is possible given the constraints.
You want to use row_number(). However, SQL tables represent unordered sets, so you need a column that specifies the ordering.
The idea is:
select l.letter, n.number
from (select l.*, row_number() over (order by ?) as seqnum
from letters l
) l join
(select n.*, row_number() over (order by ?) as seqnum
from numbers n
) n
on l.seqnum = n.seqnum;
The ? is for the column that specifies the ordering.
If you want all rows in both tables, use full join rather than an inner join.
EDTI:
row_number() is the obvious solution, but you can do this with a correlated subquery assuming the values are unique:
select l.letter, n.number
from (select l.*,
(select count(*) from letters l2 where l2.letter <= l.letter) as seqnum
from letters l
) l join
(select n.*,
(select count(*) from numbers n2 where n2.num <= n.num) as seqnum
from numbers n
) n
on l.seqnum = n.seqnum;
I find the restriction on not using row_number() to be rather absurd, given that it is an ISO/ANSI standard function supported by almost all databases.
If your version of SQL supports an ASCII function (which can generate an ASCII code for each lowercase letter), then you may join on the ASCII code shifted downwards by 96:
SELECT
t1.letters,
t2.nums
FROM table1 t1
INNER JOIN table2 t2
ON t2.nums = ASCII(t1.letters) - 96;
Demo
Related
I'm working on northwind database.
I have to select the newest and the oldest order/orders (could be multiple with same date).
Is it possible without using subqueries and top 1, only by joining tables?
Using a CTE:
WITH orders_rnk AS (
SELECT *, RANK() OVER (ORDER BY order_date ASC) AS rnk
FROM orders
)
SELECT * from orders_rn WHERE rnk = 1;
Using a JOIN:
SELECT o.*
FROM orders AS o
LEFT OUTER JOIN orders AS o2
ON o.order_date > o2.order_date
WHERE o2.order_id IS NULL;
Yes you can use TOP 1 but add WITH TIES keyword.
link to MS documentation
Per MS documentation
WITH TIES Returns two or more rows that tie for last place in the
limited results set. You must use this argument with the ORDER BY
clause. WITH TIES might cause more rows to be returned than the value
specified in expression. For example, if expression is set to 5 but
two additional rows match the values of the ORDER BY columns in row 5,
the result set will contain seven rows.
Exmaple
SELECT TOP 1 WITH TIES
FROM tableA A JOIN TableB
ON A.ID=B.ID
ORDER BY A.column1
I have 2 SQL table.Table A and Table B. Both of this table have 10000 records respectively.
In Table A have 3 Column=>
ColumnA,ColumnB,ColumnC
In Table B have 3 Column=>
ColumnD,ColumnE,ColumnF
My Requirement is to select ColumnA and ColumnD with their original record(10000).
My question is how can I select only ColumnA and ColumnD.
The first problem is I cant join this two table because this two table stands as separately.
The second problem is I cant Union this two table because my requirement is to get Two column but when I union, I only get one column with combining two column.
You could create an on-the-fly join column via ROW_NUMBER, and then join on that:
WITH cte1 AS (
SELECT ColumnA, ROW_NUMBER() OVER (ORDER BY ColumnA) rn
FROM TableA
),
cte2 AS (
SELECT ColumnD, ROW_NUMBER() OVER (ORDER BY ColumnD) rn
FROM TableB
)
SELECT t1.ColumnA, t2.ColumnD
FROM cte t1
INNER JOIN cte t2
ON t1.rn = t2.rn;
Of course, this would just pair the 10K records using the arbitrary orderings in the A and D columns. If you have some specific logic for how these two columns should be paired up, then let us know. The bottom line is that you can't easily get away from the concepts of join or union to bring these two columns together.
Hi I have the below query in Teradata. I have a row number partition and from that I want rows with rn=1. Teradata doesn't let me use the row number as a filter in the same query. I know that I can put the below into a subquery with a where rn=1 and it gives me what I need. But the below snippet needs to go into a larger query and I want to simplify it if possible.
Is there a different way of doing this so I get a table with 2 columns - one row per customer with the corresponding fc_id for the latest eff_to_dt?
select cust_grp_id, fc_id, row_number() over (partition by cust_grp_id order by eff_to_dt desc) as rn
from table1
Have you considered using the QUALIFY clause in your query?
SELECT cust_grp_id
, fc_id
FROM table1
QUALIFY ROW_NUMBER()
OVER (PARTITION BY cust_grp_id
ORDER BY eff_to_dt desc)
= 1;
Calculate MAX eff_to_dt for each cust_grp_id and then join result to main table.
SELECT T1.cust_grp_id,
T1.fc_id,
T1.eff_to_dt
FROM Table1 AS T1
JOIN
(SELECT cust_grp_id,
MAX(eff_to_dt) AS max_eff_to_dt
FROM Table
GROUP BY cust_grp_id) AS T2 ON T2.cust_grp_id = T1.cust_grp_id
AND T2.max_eff_to_dt = T1.eff_to_dt
You can use a pair of JOINs to accomplish the same thing:
INNER JOIN My_Table T1 ON <some criteria>
LEFT OUTER JOIN My_Table T2 ON <some criteria> AND T2.eff_to_date > T1.eff_to_date
WHERE
T2.my_id IS NULL
You'll need to sort out the specific criteria for your larger query, but this is effectively JOINing all of the rows (T1), but then excluding any where a later row exists. In the WHERE clause you eliminate these by checking for a NULL value in a column that is NOT NULL (in this case I just assumed some ID value). The only way that would happen is if the LEFT OUTER JOIN on T2 failed to find a match - i.e. no rows later than the one that you want exist.
Also, whether or not the JOIN to T1 is LEFT OUTER or INNER is up to your specific requirements.
So I think I've seen a solution to this however they are all very complicated queries. I'm in oracle 11g for reference.
What I have is a simple one to many join which works great however I don't need the many. I just want the left table (the one) to just join any 1 row which meets the join criteria...not many rows.
I need to do this because the query is in a rollup which COUNTS so if I do the normal left join I get 5 rows where I only should be getting 1.
So example data is as follows:
TABLE 1:
-------------
TICKET_ID ASSIGNMENT
5 team1
6 team2
TABLE 2:
-------------
MANAGER_NAME ASSIGNMENT_GROUP USER
joe team1 sally
joe team1 stephen
joe team1 louis
harry team2 ted
harry team2 thelma
what I need to do is join these two tables on ASSIGNMENT=ASSIGNMENT_GROUP but only have 1 row returned.
when I do a left join I get three rows returned beaucse that is the nature of hte left join
If oracle supports row number (partition by) you can create a sub query selecting where row equals 1.
SELECT * FROM table1
LEFT JOIN
(SELECT *
FROM (SELECT *,
ROW_NUMBER()
OVER(PARTITION BY assignmentgroup ORDER BY assignmentgroup) AS Seq
FROM table2) a
WHERE Seq = 1) v
ON assignmet = v.assignmentgroup
You could do something like this.
SELECT t1.ticket_id,
t1.assignment,
t2.manager_name,
t2.user
FROM table1 t1
LEFT OUTER JOIN (SELECT manager_name,
assignment_group,
user,
row_number() over (partition by assignment_group
--order by <<something>>
) rnk
FROM table2) t2
ON ( t1.assignment = t2.assignment_group
AND t2.rnk = 1 )
This partitions the data in table2 by assignment_group and then arbitrarily ranks them to pull one arbitrary row per assignment_group. If you care which row is returned (or if you want to make the row returned deterministic) you could add an ORDER BY clause to the analytic function.
I think what you need is to use GROUP BY on the ASSIGNMENT_GROUP field.
http://www.w3schools.com/sql/sql_groupby.asp
In MySQL you could just GROUP BY ASSIGNMENT and be done. Oracle is more strict and refuses to just choose (in an undefined way) which values of the three rows to choose. That means all returned columns need to be part of GROUP BY or be subject to an aggregat function (COUNT, MIN, MAX...)
You can of course choose to just don't care and use some aggregat function on the returned columns.
select TICKET_ID, ASSIGNMENT, MAX(MANAGER_NAME), MAX(USER)
from T1
left join T2 on T1.ASSIGNMENT=T2.ASSIGNMENT_GROUP
group by TICKET_ID, ASSIGNMENT
If you do that I would seriously doubt that you need the JOIN in the first place.
MySQL could also help with GROUP_CONCAT in the case that you want a string concatenation of group values for a column (humans often like that), but with Oracle that is staggeringly complex.
Using a subquery as already suggested is an option, look here for an example. It also allows you to sort the subquery before selecting the top row.
In Oracle, if you want 1 result, you can use the ROWNUM statement to get the first N values of a query e.g.:
SELECT *
FROM TABLEX
WHERE
ROWNUM = 1 --gets the first value of the result
The problem with this single query is that Oracle never returns the data in the same order. So, you must oder your data before use rownum:
SELECT *
FROM
(SELECT * FROM TABLEX ORDER BY COL1)
WHERE
ROWNUM = 1
For your case, looks like you only need 1 result, so your query should look like:
SELECT *
FROM
TABLE1 T1
LEFT JOIN
(SELECT *
FROM TABLE2 T2 WHERE T1.ASSIGNMENT = T2.ASSIGNMENT_GROUP
AND
ROWNUM = 1) T3 ON T1.ASSIGNMENT = T3.ASSIGNMENT_GROUP
you can use subquery - select top 1
I have two tables which I would like to place side-by-side exactly as they are. For example,
tableOne tableTwo
columnOne | columnTwo | columnThree columnI | columnII | columnIII
The data in the two tables do not need to be related whatsoever -- the tables have the same row count -- and the data is already sorted in the two tables. Basically, I would like to do a full outer join on the two tables without an on operator.
How can I do this in a SQL query?
Well, you do want an ON operator - you just seem to want it to work automatically, which won't happen.
If you're saying Row 1 of tableOne maps to Row 1 of tableTwo, then you need to add a row column to each table and then join on it.
If you don't specify a join condition, you'll do a cross join that joins every row from tableOne to every row in tableTwo, which obviously isn't what you're looking for.
So do something like this:
select * from
(select *, row_number() over (order by 1) as RN from tableOne) a
inner join (select *, row_number() over (order by 1) as RN from tableTwo) b
on a.RN = b.RN