I need your help. I have a table named Test_Result with 2 columns as shown below.
ID Source_ID
10 1
20 2
30 2
40 3
50 3
60 3
70 4
I am trying to get output as below,but unable to get logic.
ID Parent_ID Source_ID
10 Null 1
20 Null 2
30 20 2
40 Null 3
50 40 3
60 50 3
70 Null 4
Kindly help me with this scenario. I attached question in picture for as well.
Regards,
Abhi
These solutions (ROW_NUMBER/LAG) will work for MySQL 8.0+ or MariaDB 10.2
You could use ROW_NUMBER() and join to previous row:
CREATE TABLE tab(ID INT ,Source_ID INT);
INSERT INTO tab(id, Source_id)
SELECT 10, 1
UNION ALL SELECT 20 , 2
UNION ALL SELECT 30, 2
UNION ALL SELECT 40 , 3
UNION ALL SELECT 50 , 3
UNION ALL SELECT 60 , 3
UNION ALL SELECT 70 , 4;
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(ORDER BY id) AS rn
FROM tab
)
SELECT c1.ID,
CASE WHEN c1.Source_ID = c2.Source_ID THEN c2.Id END AS Parent_Id,
c1.Source_ID
FROM cte c1
LEFT JOIN cte c2
ON c1.rn = c2.rn+1;
Rextester Demo
EDIT:
Using LAG() windowed function:
SELECT c1.ID,
CASE
WHEN c1.Source_ID = LAG(Source_ID) OVER w THEN LAG(ID) OVER w
END AS Parent_Id,
c1.Source_ID
FROM tab c1
WINDOW w AS (ORDER BY ID)
ORDER BY id;
DBFiddle
EDIT2:
Simulating LAG using variables:
SET #lag_Source_id='';
SET #lag_Id = '';
SELECT ID,
CASE WHEN Source_Id = lag_Source_ID THEN lag_ID END AS Parent_ID
,Source_ID
FROM (
SELECT ID
, Source_ID
, #lag_Source_id AS lag_Source_id
, #lag_Source_id:= Source_ID AS curr_Source_ID
, #lag_Id AS lag_ID
, #lag_Id := ID AS curr_ID
FROM tab
ORDER BY id
) AS sub
RextesterDemo2
if you are using mysql database simply do this,
SELECT ID, (ID + Source_ID) AS Parent_ID, Source_ID FROM tableName LIMIT 10;
Related
Suppose I have a table
id value
------ ---------
10 123
10 422
11 441
11 986
12 674
13 648
I need a query which will return only those id's which have 2 or more values associated with them. So, in that case it will only return ID 10 & 11, but i need al the records.
so the result looks like:
id value
------ ---------
10 123
10 422
11 441
11 986
Thank you.
select a2.*
from MyTable a2
inner join
(
select a1.id
from MyTable a1
group by a1.id
having count(*) > 1
) a3
on a3.id = a2.id
Assuming a UNIQUE KEY can be formed on (id,value)...
SELECT DISTINCT x.*
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.value <> x.value
If a UNIQUE KEY cannot be formed on (id,value), then this isn't really a table in a strict RDBMS sense.
You can use this query :
SELECT * from table where id in
( SELECT id FROM table group by id having count(id) > 1 )
With mysql 8+ or mariadb 10.2+, you would use the count window function:
select id, value
from (
select id, value, count(id) over (partition by id) as num_values
from sometable
) foo
where num_values > 1;
My data is:
bid_id, fkp_id, fkb_id, bid_amount
1 , 13 , 1 , 22000
2 , 13 , 2 , 23000
3 , 13 , 2 , 23000
4, 3 , 1 , 5000
5, 3 , 2 , 6000
If there is a tie (in this case bid_id 2 and bid_id 3) this record should not be selected and only unique max bid_amount value record should be selected.
in this case desired record is bid_id 1 and bid_id 5
Looking to your data if you need all with one unique max amount
and the max amount is related to the amount itself you could use
select bid_id, fkp_id, fkb_id, bid_amount
from table_name
where bid_amount = ( select max(t.bid_amount)
from ( select bid_amount, count(*)
from table_name
group by bid_amount
having count(*) = 1 ) t )
If your unique max amount is for flp_id then you shoulf group by properly for this columns too
select bid_id, fkp_id, fkb_id, bid_amount
from table_name
where ( fkp_id, bid_amount) in ( select t.fkp_id, max(t.bid_amount)
from ( select fkp_id, bid_amount, count(*)
from table_name
group by fkp_id, bid_amount
having count(*) = 1 ) t
group by t.fkp_id )
Id | Price
----------------
1 | 10
2 | 20
3 | 40
4 | 10
I need to select ids where first occurrence of summation of price is greater than or equal 55 matching from the bottom. At this case --
I will have 4,3,2 ids selected.
Well, this is kinda tricky for MySQL since it doesn't support any window fuctions and becuase you want to include the first occurrence as well. You can try this:
SELECT * FROM (
SELECT t.id,
(SELECT sum(s.price) FROM YourTable s
WHERE s.id <= t.id) as cuml_sum
FROM YourTable t) ss
WHERE ss.cuml_sum < 55
--Will select all the record will the sum < 55
UNION ALL
SELECT * FROM (
SELECT t.id,
(SELECT sum(s.price) FROM YourTable s
WHERE s.id <= t.id) as cuml_sum
FROM YourTable t) tt
WHERE tt.cuml_sum >= 55
ORDER BY tt.cuml_sum
LIMIT 1
--Will select the first record that have sum >= 55
I need to pull the name of the students who stood second positions from grade 1 to grade 12. each grade has separate databases with similar table structure
I have the following data:
Set 1
uid marks
1 10
2 20
3 17
4 17
5 20
6 20
Set 2
uid marks
1 10
2 20
3 17
4 17
5 20
6 17
7 20
I need a query which can say uid 3,4 are second in set 1 and 3,4,6 are second in set 2.
i need it in a single query because there are several set of databases
what could be the possible way?
I tried:
SELECT * FROM TBL WHERE marks ! = SELECT MAX(marks) from tbl
but it fetched all marks except the highest
Try this out:
SELECT uid, marks FROM (
SELECT uid, marks, #rank := #rank + (#prevMarks != marks) rank, #prevMarks := marks
FROM t, (SELECT #rank := 0, #prevMarks := 0) init
ORDER BY marks
) s
WHERE rank = 2
Fiddle here.
Another alternative without User Defined Variables:
SELECT t.uid, t.marks FROM t
JOIN (
SELECT DISTINCT marks FROM t
ORDER BY marks
LIMIT 1, 1
) s
ON t.marks = s.marks
Output:
| UID | MARKS |
|-----|-------|
| 3 | 17 |
| 4 | 17 |
Use LIMIT and ORDER BY
SELECT * FROM TBL ORDER BY marks DESC LIMIT 1,1
There you ordered all students by marks fro hi to low. And then limit return from second (0 is first record) and return only one record.
If need all students with second mark, the use subquery
SELECT * FROM TBL WHERE marks = (
SELECT marks FROM TBL ORDER BY marks DESC GROUP BY marks LIMIT 1,1
)
SELECT *
FROM table
WHERE mark = (
SELECT MAX(mark)
FROM table
WHERE mark <
(
SELECT MAX(mark)
FROM table
)
)
Try this
SELECT t.marks, t.uid, (
SELECT COUNT( marks ) +1
FROM tbl t1
WHERE t.marks < t1.marks
) AS rank
FROM tbl t
LIMIT 0 , 30
now you can use rank column with bit modification below
SELECT * from (
SELECT t.marks, t.uid, (
SELECT COUNT( marks ) +1
FROM tbl t1
WHERE t.marks < t1.marks
) AS rank
FROM tbl t
) alias where rank=n (2 here)
This would be relatively easy if I only cared about a single min and max for each group, the problem is my requirement is to find the various boundaries. An example data set is as follows:
BoundaryColumn GroupIdentifier
1 A
3 A
4 A
7 A
8 B
9 B
11 B
13 A
14 A
15 A
16 A
What I need from the sql is a result set as follows:
min max groupid
1 7 A
8 11 B
13 16 A
Essentially finding the boundaries for each cluster of the groups.
The data would be stored in either oracle11g or mysql so syntax can be provided for either platform.
A disclaimer: It's a lot easier to query partial results and process something like this with a front-end language. That said...
The following query works for Oracle (which supports analytic queries) but not for MySQL (which does not). There's a SQL Fiddle here.
WITH BoundX AS (
SELECT * FROM (
SELECT
BoundaryColumn,
GroupIdentifier,
LAG(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLag,
LEAD(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLead
FROM MyTable
ORDER BY BoundaryColumn
)
WHERE GIDLag IS NULL OR GroupIdentifier <> GIDLag
OR GIDLead IS NULL OR GroupIdentifier <> GIDLead
)
SELECT MIN, MAX, GROUPID
FROM (
SELECT
BoundaryColumn AS MIN,
LEAD(BoundaryColumn) OVER (ORDER BY BoundaryColumn) AS MAX,
GroupIdentifier AS GROUPID,
GIDLag,
GIDLead
FROM BoundX
)
WHERE GROUPID = GIDLead
Here's the logic, step by step. You may be able to improve on this, because I get the feeling there's one subquery too many here...
This query pulls the prior and following GroupIdentifier values into each row:
SELECT
BoundaryColumn,
GroupIdentifier,
LAG(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLag,
LEAD(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLead
FROM MyTable
ORDER BY BoundaryColumn
The result looks like this:
BoundaryColumn GroupIdentifier GIDLag GIDLead
1 A A
3 A A A
4 A A A
7 A A B
8 B A B
9 B B B
11 B B A
13 A B A
14 A A A
15 A A A
16 A A
If you add logic to get rid of all the rows where GIDLag = GIDLead = GroupIdentifier, you'll end up with the boundaries:
WITH BoundX AS (
SELECT * FROM (
SELECT
BoundaryColumn,
GroupIdentifier,
LAG(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLag,
LEAD(GroupIdentifier) OVER (ORDER BY BoundaryColumn) AS GIDLead
FROM MyTable
ORDER BY BoundaryColumn
)
WHERE GIDLag IS NULL OR GroupIdentifier <> GIDLag
OR GIDLead IS NULL OR GroupIdentifier <> GIDLead
)
SELECT
BoundaryColumn AS MIN,
LEAD(BoundaryColumn) OVER (ORDER BY BoundaryColumn) AS MAX,
GroupIdentifier AS GROUPID,
GIDLag,
GIDLead
FROM BoundX
With this addition the results are:
MIN MAX GROUPID GIDLAG GIDLEAD
--- --- ------- ------ -------
1 7 A A
7 8 A A B
8 11 B A B
11 13 B B A
13 16 A B A
16 A A
Finally, include only those rows where GroupID = GIDLead. That's the query at the top of this answer. The results are:
MIN MAX GROUPID
--- --- -------
1 7 A
8 11 B
13 16 A
Take a look at this site regarding "runs" of data: http://www.sqlteam.com/article/detecting-runs-or-streaks-in-your-data
Armed with the knowledge provided in that link, you could write a query like this:
SELECT BoundaryColumn,
GroupIdentifier,
(
SELECT COUNT(*)
FROM Table T
WHERE T.GroupIdentifier <> TR.GroupIdentifier
AND T.BoundaryColumn <= TR.BoundaryColumn
) as RunGroup
FROM Table TR
Using this information, you could then group by "RunGroup", and select the GroupIdentifier and min/max BoundaryColumn.
EDIT: I've felt the peer pressure, here's an SQLFiddle with my version of the answer: http://www.sqlfiddle.com/#!8/9a24c/4/0
Another approach(Oracle). Here we simply divide result set returned by the query issued against table t1(your table) into logical groups(grp). Each new group starts when a value of GroupIdentifier changes:
select min(q.BoundaryColumn) as MinB
, max(q.BoundaryColumn) as MaxB
, max(q.GroupIdentifier) as groupid
from ( select s.BoundaryColumn
, s.GroupIdentifier
, sum(grp) over(order by s.BoundaryColumn) as grp
from ( select BoundaryColumn
, GroupIdentifier
, case
when GroupIdentifier <> lag(GroupIdentifier)
over(order by BoundaryColumn)
then 1
end as grp
from t1) s
) q
group by q.grp
Result:
MINB MAXB GROUPID
---------- ---------- -------
1 7 A
8 11 B
13 16 A
SQLfiddle Demo