How to recursively look up value from within a join after getting value from another table? - sql

I have a table that looks like this:
T1
pid reason cid
-----------------------
1 aaa C1
1 bbb C2
I want to select the cid with greatest value to get to the reason. I can select the greatest cid like so:
SELECT MAX(TRIM('C' from cid)) AS id
FROM T1
However when I try to introduce the reason column like so:
SELECT reason, MAX(TRIM('C' from cid)) AS id
FROM T1
GROUP BY reason
I get the following result:
reason cid
---------------
aaa 1
bbb 2
I only need the reason where cid equals 2.
To add more complexity I only want to extract the reason field from the greatest cid from within a LEFT join.
select t2.*
from t2
left join (select to pick `reason` with greatest `cid`)
on t2.pid = t1.pid
How do I take the greatest value in cid from T1, look up the reason field and pull it into T1 via the left join?

It looks like reason isn't used at all in your final query, but maybe that's just a typo?
Use limit 1 to get the last row:
SELECT pid, reason, cid FROM T1 order by cid DESC limit 1;
Then, your final query could be something like this:
select t2.*, reason
from t2
left join (select pid, reason FROM T1 order by cid DESC limit 1) as sub on sub.pid = t2.pid;
If cid is not unique, you may want to add more columns to the order by clause to make your result deterministic.

The query would be
SELECT reason,
trim('C' from cid)
FROM t1
ORDER BY trim('C' from cid) DESC
LIMIT 1;
I cannot answer the part about the outer join, because the requirement is too vague.

You can use the HAVING clause:
SELECT reason, MAX(trim('C' from cid)) as id
FROM T1
GROUP BY reason
HAVING MAX(trim('C' from cid)) = 2;

Related

Select max value from a table and increment it in another

I'm struggling with a sql query, and will appreciate any help.
I have two tables, they both have a sort column. The first one looks like this:
person_id
image_name
sort_number
739
chest.png
1
739
legs.png
2
And the second table like this
person_id
advert
sort_number
739
house.png
1
739
car.png
2
I want to be able to select the max sort_number from the table1 and make the first sort_number in table2 (house.png) to become 3, and the sort_number for car.png) to become 4.
Essentially, what I'm looking to achieve is an insert statement that selects from table2 and insert into table1, but I need the sort_number not to have duplicate, so the starting sort_number from the table2 should be the max of table1+1...and so on. If table1 does not have the person, I simply insert and not change the sort_number value.
I would appreciate of someone can help me please.
Here's one way:
With grouped as
(Select person_id, max(sort_num) as maxsort
From table1
Group by person_id)
Select t2.person_id, t2.advert, t2.sort_num + coalesce(g.maxsort,0) as newsortnum
From table t2
Left join grouped g on t2.person_id = g.person_id
This will get max value of sort number for each key in the first table, and then attempt to join the second table to this grouped dataset. If there is a match, you add your second table's value to the max, and retain the value from the second table otherwise.
You could try using UNION ALL and ROW_NUMBER:
WITH CTE AS
(
SELECT
person_id,
image_name,
sort_number,
1 sort_table
FROM dbo.Table1 t1
UNION ALL
SELECT
person_id,
advert,
sort_number,
2
FROM dbo.Table2 t2
)
SELECT
person_id,
image_name,
ROW_NUMBER() OVER(PARTITION BY person_id ORDER BY sort_table, sort_number) sort_number
FROM CTE
;
insert into table2(person_id, advert, sort_number)
select table1.person_id, table1.image_name, table1.sort_number + table2.sort_number
from table1
join table2
on 1 = 1
left join table2 newer
on table2.sort_number < newer.sort_number
left join table1 mismatch
on table2.person_id = mistmatch.person_id and table2.advert = mismatch.image_name
where newer.person_id is null and mismatch.person_id is null
Firs join: we need to pair table1 and table2
Second join: we make sure that table2 record in the pair is the newest
Third join: we make sure that we do not insert what's already there.

SQL Find most rows that match between two tables

I am using SQL Server 2012 I have two tables like the following
Table1 and Table 2 both have many groups, indicated by the group column. The name of the group may match in both tables, but it may not. What is important is finding the group on Table 2 that has the most members that match members in a group on Table1.
I first tried doing this with a vlookup, but the problem is vlookup pulls the first entry in the Group column that has a match, not the group with the most matches. Below vlookup would pull BBB, but the correct result is CCC.
Ties may occur. There might be more than one group on Table2 that match Table1 with the same number of members thus the best thing may be to count the number of matches, but there are thousands of groups so it's not ideal to sort and sift through a column with counts. I need something like a case statement where if there is a MAX(match) then Table1 would show the group name with MAX(Match) in the derived column BestMatch. It'd be most ideal if the column could display all the groups on table 2 that have MAX(Match which may be one or more. Perhaps it could be comma separated.
If not if the column could just say tie and I could look for the tie, it'd be ideal if this is the best option, when the word tie appears it repeats besides every member that matches so I will know to look for groups that matching which accounts and how many that matched.
We really could do with some expected output to help clarify the question.
If I understand you correctly however, this query will get you close to the results you require:
;with cte as
( SELECT t1a.[group] AS Group1
, t2a.[Group] AS Group2
, RANK() OVER(PARTITION BY t1a.[group]
ORDER BY COUNT(t2a.[Group]) DESC) AS MatchRank
FROM Table1 t1a
JOIN Table2 t2a
ON t1a.member = t2a.member
GROUP BY t1a.[group], t2a.[GRoup])
SELECT *
FROM cte
WHERE MatchRank=1
The query doesn't identify ties, but it will display any tied results...
If you are a newbie to common table expressions(the ;with statement) there is a useful description here.
select *
from Table1 t1
outer apply
(
select top 1 t2.[Group]
from Table2 t2
where t2.Member = t1.Member
group by t2.[Group]
order by count(*) desc
) m
It may not be the most elegant solution but I think it could do the work:
select *
from
(select t1.[group] as t1group, t1.member, t2.[group] as t2group
from Table1 t1 inner join Table2 t2 on t1.member = t2.member)a
where member = (select max(t1.member)
from Table1 t1 inner join Table2 t2 on t1.member = t2.member)
In case of 2 rows from Table2 matching the maximum members in Table1, both results would be displayed
PS: an example of your desired results would have been helpful
Count member matches per group pair and rank them so the group pairs with the highest match count get rank #1. Once you found these, you can select the related records from table1 and table2.
select t1.grp, t1.member, t2.grp
from t1
join
(
select
t1.grp as grp1,
t2.grp as grp2,
rank() over (order by count(*) desc) as rnk
from t1
join t2 on t2.member = t1.member
group by t1.grp, t2.grp
) grps on grps.rnk = 1 and grps.grp1 = t1.grp
left join t2 on t2.grp = grps.grp2 and t2.member = t1.member
order by t1.grp, t1.member, t2.grp;
This gives you ties in separate rows, e.g. for AAA having four different members (123,456,789,555) with two matches both in CCC and DDD:
grp1 member grp2
AAA 123 CCC
AAA 123 DDD
AAA 456 CCC
AAA 789
AAA 555 DDD
If you want one row per grp1 and member with all matching grp2 in a string then you need some clumsy STUFF trick in SQL Server as far as I am aware. Look up "GROUP_CONCAT in SQL Server" to find the technique needed.

SQL - get max result

Assume there is a table name "test" below:
name value
n1 1
n2 2
n3 3
Now, I want to get the name which has the max value, I have some solution below:
Solution 1:
SELECT TOP 1 name
FROM test
ORDER BY value DESC
solution 2:
SELECT name
FROM test
WHERE value = (SELECT MAX(value) FROM test);
Now, I hope use join operation to find the result, like
SELECT name
FROM test
INNER JOIN test ON...
Could someone please help and explain how it works?
If you are looking for JOIN then
SELECT T.name, T.value
FROM test T
INNER JOIN
( SELECT T1.name, T1.value ,
RANK() OVER (PARTITION BY T1.name ORDER BY T1.value) N
FROM test T1
WHERE T1.value IN (SELECT MAX(t2.value) FROM test T2)
)T3 ON T3.N = 1 AND T.name = T3.name
FIDDLE DEMO
or
select name, value
from
(
select name, value,
row_number() over(order by value desc) rn
from test
) src
where rn = 1
FIDDLE DEMO
First, note that solutions 1 and 2 could give different results when value is not unique. If in your test data there would be an additional record ('n4', 3), then solution 1 would return either 'n3' or 'n4', but solution 2 would return both.
A solution with JOIN will need aliases for the table, because as you started of, the engine would say Ambiguous column name 'name'.: it would not know whether to take name from the first or second occurrence of the test table.
Here is a way to complete the JOIN version:
SELECT t1.name
FROM test t1
LEFT JOIN test t2
ON t2.value > t1.value
WHERE t2.value IS NULL;
This query takes each of the records, and checks if any records exist that have a higher value. If not, the first record will be in the result. Note the use of LEFT: this denotes an outer join, so that records from t1 that have no match with t2 -- based on the ON condition -- are not immediately rejected (as would be the case with INNER): in fact, we want to reject all the other records, which is done with the WHERE clause.
A way to understand this mechanism, is to look at a variant of the query above, which lacks the WHERE clause and returns the values of both tables:
SELECT t1.value, t2.value
FROM test t1
LEFT JOIN test t2
ON t2.value > t1.value
On your test data this will return:
t1.value t2.value
1 2
1 3
2 3
3 (null)
Note that the last entry would not be there if the join where an INNER JOIN. But with the outer join, one can now look for the NULL values and actually get those records in the result that would be excluded from an INNER JOIN.
Note that this query will give the same result as solution 2 when there are duplicate values. If you want to have also only one result like with solution 1, it suffices to add TOP 1 after SELECT.
Here is a fiddle.
Alternative with pure INNER JOIN
If you really want an INNER join, then this will do it. Again the TOP 1 is only needed if you have non-unique values:
SELECT TOP 1 t1.name
FROM test t1
INNER JOIN (SELECT Max(value) AS value FROM test) t2
ON t2.value = t1.value;
But this one really is very similar to what you did in solution 2. Here is fiddle for it.

how to get single row form group

I want a query which will give me desired output ,I am using following query
select table1.name1,table2.address
from table1 join table2
on(table1.key=table2.key);
it is giving me result After join nut I only need single row for each key.
Any help will be appreciated.
If any combination is allowed but only one row for each key then try this:
select table1.name1, MAX(table2.address) address
from table1
join table2
on table1.key=table2.key
group by table1.name1
try like this
select *
from (select table2.address,
rank() over ( partition by table1.name1 order by table1.key) rn
from table1 join table2 on(table1.key=table2.key))
where rn = 1

How do I limit the number of rows returned by this LEFT JOIN to one?

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