I have following 3 tables with duplicate id.I want to retrieve record with same id but having different name and date from all the 3 tables.I need query to get Expected result output.
CREATE TABLE Student1
(`id` int,`status` int,`amount` int , `Name` varchar(10), `date` varchar(55))
;
INSERT INTO Student1
(`id`,`status`,`amount`, `Name`, `date`)
VALUES
(1,0,4500, 'ram', '04/02/2012'),
(2,0,2000, 'shyam', '05/09/2013'),
(4,0,1500, 'ghanshyam', '08/11/2014')
;
CREATE TABLE Student2
(`id` int,`status` int,`amount` int , `Name` varchar(10), `date` varchar(55))
;
INSERT INTO Student2
(`id`,`status`,`amount`, `Name`, `date`)
VALUES
(3,0,4500, 'gopal', '04/02/2012'),
(2,0,8000, 'radheshyam', '15/11/2013'),
(4,1,1500, 'ghanshyam', '18/10/2015')
;
CREATE TABLE Student3
(`id` int,`status` int,`amount` int , `Name` varchar(10), `date` varchar(55))
;
INSERT INTO Student3
(`id`,`status`,`amount`, `Name`, `date`)
VALUES
(1,1,4500, 'ram', '14/02/2012'),
(2,0,6500, 'radhe', '11/11/2014'),
(3,1,4500, 'gopal', '14/02/2015')
;
Excepted Result :
id status amount Name date
2 0 2000 shyam 05/09/2013
2 0 6500 radhe 11/11/2014
2 0 8000 radheshyam 15/11/2013
You just use union all to bring the tables together. One way is:
select s.*
from (select s.* from student1 s union all
select s.* from student2 s union all
select s.* from student3 s
) s
where id = 2;
As I say in the comment, though, normally you would have three tables rather than one.
I realize that I might have misunderstood the question. If you want to find records that have the same id but different names, then use:
select s.id, group_concat(s.name) as names
from (select s.* from student1 s union all
select s.* from student2 s union all
select s.* from student3 s
) s
group by s.id
having count(distinct name) = 3 -- or perhaps >= 2, depending on what you mean
If you want the full records, you can join this back to the original tables.
EDIT:
If you want all the original rows:
select s.*
from (select s.id, group_concat(s.name) as names
from (select s.* from student1 s union all
select s.* from student2 s union all
select s.* from student3 s
) s
group by s.id
having count(distinct name) = 3
) ss join
(select s.* from student1 s union all
select s.* from student2 s union all
select s.* from student3 s
) s
on ss.id = s.id;
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I am trying to generate row number for the records but for the same record I am getting 2 unique row number. Is it possible if records are same then it generate the same unique number?
Example:
GENERIC_ID|Generic Name|HTA_INSERT_DT
2|Eculizumab|2021-02-10 05:50:28
7|Eculizumab|2021-02-10 05:50:28
9|Eculizumab|2021-02-10 05:50:28
in final output I want-
GENERIC_ID|Generic Name|HTA_INSERT_DT
2|Eculizumab|2021-02-10 05:50:28
2|Eculizumab|2021-02-10 05:50:28
2|Eculizumab|2021-02-10 05:50:28
I am running below sql query-
SELECT ROW_NUMBER() OVER(Order by (select NULL)) as GENERIC_ID
, a.`Generic Name`
, date_format(current_timestamp(),'yyyy-MM-dd hh:mm:ss') as HTA_INSERT_DT
from
(
Select distinct `Generic Name` from vw_non_onco_pharma
Union
Select distinct `Generic Name` from vw_plasma_protein
Union
Select distinct `Generic Name` from vw_non_onco_cell_gene
Union
Select distinct `Generic Name` from vw_onco_cell_gene
Union
Select distinct `Generic Name` from vw_onco_pharma
Union
Select distinct `Generic Name` from vw_non_onco_no_id
Union
Select distinct `Generic Name` from vw_onco_no_id
) a
You can DENSE_RANK() the records ordered by your key fields to get matching ids in sets. If you want the duplicates to take away from the sequential counter then use RANK() instead.
DECLARE #X TABLE(ID1 INT, ID2 INT)
INSERT #X VALUES(1,1),(2,1),(2,2),(1,2),(1,2),(1,3),(1,1)
SELECT
R = RANK() OVER(PARTITION BY 1 ORDER BY ID1,ID2),
DR = DENSE_RANK() OVER(PARTITION BY 1 ORDER BY ID1,ID2),
ID1,
ID2
FROM
#X
R DR ID1 ID2
1 1 1 1
1 1 1 1
3 2 1 2
3 2 1 2
5 3 1 3
6 4 2 1
7 5 2 2
However, after re-reading your question. I now think you are looking for something similar to adding a derived key to id your unioned records like below.
SELECT
DENSE_RANK() OVER(PARTITION BY 1 ORDER BY a.`Generic Name`,date_format(current_timestamp(),'yyyy-MM-dd hh:mm:ss')) as GENERIC_ID
, a.`Generic Name`
, date_format(current_timestamp(),'yyyy-MM-dd hh:mm:ss') as HTA_INSERT_DT
from
(
Select distinct `Generic Name` from vw_non_onco_pharma
Union
Select distinct `Generic Name` from vw_plasma_protein
Union
Select distinct `Generic Name` from vw_non_onco_cell_gene
Union
Select distinct `Generic Name` from vw_onco_cell_gene
Union
Select distinct `Generic Name` from vw_onco_pharma
Union
Select distinct `Generic Name` from vw_non_onco_no_id
Union
Select distinct `Generic Name` from vw_onco_no_id
) a
ROW_NUMBER() will always apply a sequence of numbers, even if the data is the same on row level.
You will have to create an unique list with row numbers, then join the full list based on Generic Name and HTA_INSERT_DT colums. They query will apply the same sequence number for matching generic name and insert date time.
I'm not clear if your tables contain multiple distinct generic_names.
You could in a cte get the min row number for each generic_name then union and join in the main query
drop table if exists t1,t,t2;
create table t(Generic_Name varchar(20));
create table t1(Generic_Name varchar(20));
create table t2(Generic_Name varchar(20));
insert into t values('aaa'),('Eculizumab');
insert into t1 values('Eculizumab');
insert into t2 values('Eculizumab');
with cte as
(
select generic_name,min(rn) generic_id,current_timestamp ht
from
(
select row_number() over (order by generic_name) rn,
generic_name
from
(
select generic_name from t
union
select generic_name from t1
union
select generic_name from t2
) s
) u
group by generic_name
)
select generic_id,t.generic_name,ht
from cte
join t on t.generic_name = cte.generic_name
union all
select generic_id,t1.generic_name,ht
from cte
join t1 on t1.generic_name = cte.generic_name
union all
select generic_id,t2.generic_name,ht
from cte
join t2 on t2.generic_name = cte.generic_name;
+------------+--------------+---------------------+
| generic_id | generic_name | ht |
+------------+--------------+---------------------+
| 1 | aaa | 2021-02-10 14:32:50 |
| 2 | Eculizumab | 2021-02-10 14:32:50 |
| 2 | Eculizumab | 2021-02-10 14:32:50 |
| 2 | Eculizumab | 2021-02-10 14:32:50 |
+------------+--------------+---------------------+
4 rows in set (0.004 sec)
MySQL Version 8.0
Schema SQL
CREATE TABLE IF NOT EXISTS `department` (
`id` INT NOT NULL,
`name` VARCHAR(45) NOT NULL,
`father` INT NULL,
PRIMARY KEY (`id`),
INDEX `fk_department_department_idx` (`father` ASC) VISIBLE,
CONSTRAINT `fk_department_department`
FOREIGN KEY (`father`)
REFERENCES `department` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
insert into department (id,name,father)
values
(1, 'dp1',null),
(2, 'dp2',null),
(3, 'dp3',1),
(4, 'dp4',1),
(5, 'dp5',2),
(6, 'dp6',4),
(7, 'dp7',6),
(8, 'dp8',6),
(9, 'dp9',6);
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''));
SET SESSION sql_mode = '';
My query:
WITH RECURSIVE cte_department AS (
SELECT
d1.id,
d1.name,
d1.father
FROM
department d1
WHERE
d1.id=6
UNION ALL
SELECT
d2.id,
d2.name,
d2.father
FROM
department d2
INNER JOIN cte_department cte ON cte.id = d2.father
)
SELECT * FROM cte_department;
Result:
id name father
6 dp6 4
7 dp7 6
8 dp8 6
9 dp9 6
What I need:
id name father
1 dp1 null
4 dp4 1
6 dp6 4
7 dp7 6
8 dp8 6
9 dp9 6
The problem is:
I can get all childrens, but I need to add to this query all the parents from the given ID, in this case, the ID 6.
I'm stuck with that. If someone can help me, follow the fiddle.
https://www.db-fiddle.com/f/g8YkE3hqsvaw8G9vdHPyyF/0
The recursive part can have multiple query blocks.
WITH RECURSIVE cte_department AS (
SELECT
d1.id,
d1.name,
d1.father,
'Begin' state
FROM
department d1
WHERE
d1.id=6
UNION ALL
SELECT
d2.id,
d2.name,
d2.father,
'Up'
FROM
department d2
INNER JOIN
cte_department cte
ON
cte.father = d2.id
WHERE
cte.state in ('Begin', 'Up')
UNION ALL
SELECT
d2.id,
d2.name,
d2.father,
'Down'
FROM
department d2
INNER JOIN
cte_department cte
ON
cte.id = d2.father
WHERE
cte.state in ('Begin', 'Down')
)
SELECT
id, name, father
FROM
cte_department
ORDER BY
father, id, name;
Try it on db<>fiddle.
I would use two separate recursive queries: one to bring the children, the other for the parents, and then union the results. You can keep track of the level of each node to properly order the records int he resultset:
with recursive
children as (
select 1 as lvl, d.* from department d where id = 6
union all
select c.lvl, d.* from department d inner join children c on c.id = d.father
),
parents as (
select 1 as lvl, d.* from department d where id = 6
union all
select p.lvl - 1, d.* from department d inner join parents p on d.id = p.father
)
select * from parents
union -- on purpose, to remove the duplicate on id 6
select * from children
order by lvl;
This is safer than having multiple union all members in the same query. MySQL does not guarantee the order of evaluation of the members in the recursion, so using this technique could lead to unexpected behavior.
Demo on DB Fiddle
Unrelated to your question, but: the following can be seen in your code:
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''));
SET SESSION sql_mode = '';
Just don't. ONLY_FULL_GROUP_BY is there for a good reason, that is to have MySQL behave consistenly with the SQL standard as regard to aggregation query. Disabling this SQL mode is never a good idea.
I have three tables asrecord of students:
record of courses:
Map student and course:
I want to get records from table std_course where student are as 1,2,3 using query
select course_id from std_course where std_id in (1,2,3) group by course_id
It returns me course_id as 1 but here std_id=4 also exist against course_id=1
I need to select course_id where std_id are only 1,2,3
You can use sum() and select case, std_id above 3 will be 0
select * from (
select sum(case when std_id in (1, 2, 3) then 1 else 0 end) tot
, course_id
from std_course
group by course_id) t1
where t1.tot <= 3
You can try this using join:
select * from std_course left outer join course on course.id= std_course.course_id where std_id <=3
The query that work for me after the changings in query Answered by #ϻᴇᴛᴀʟ is:
select * from ( select sum(case when std_id in (1,2,3) then 1 else -1 end) tot , course_id from std_course group by course_id) t1 where t1.tot = 3
Good day.
I seem to be struggling with what seems like a simple problem.
I have a table that has a value connected to a date (Monthly) for a finite number of ID's
ie. Table1
ID | Date ---| Value
01 | 2015-01 | val1
01 | 2015-02 | val2
02 | 2015-01 | val1
02 | 2015-03 | val2
So ID: 02 does not have a value for date 2015-02.
I would like to return all ID's and Dates that do not have a value.
Date range is: select distinct date from Table1
I can't seem to think outside the realms of selecting and joining on the same table.
I need to include the ID in my select to I can somehow select the ID and Date range that exists for that ID and compare to the entire date range, to get all the dates for each ID that isn't in the "entire" date range.
Please advise.
Thank you
Not very clear about your last two sentences. But you can play with the following query with different #max_days and #min_date:
-- DROP TABLE table1;
CREATE TABLE table1(ID int not null, `date` date not null, value varchar(64) not null);
INSERT table1(ID,`date`,value)
VALUES (1,'2015-01-01','v1'),(1,'2015-01-02','v2'),(2,'2015-01-01','v1'),(2,'2015-01-03','v2'),(4,'2015-01-01','v1'),(4,'2015-01-04','v2');
SELECT * FROM table1;
SET #day=0;
SET #max_days=5;
SET #min_date='2015-01-01';
SELECT i.ID,d.`date`
FROM (SELECT DISTINCT ID FROM table1) i
CROSS JOIN (
SELECT TIMESTAMPADD(DAY,#day,#min_date) AS `date`,#day:=#day+1 AS day_num
FROM table1 WHERE #day<#max_days) d
LEFT JOIN table1 t
ON t.ID=i.ID
AND t.`date`=d.`date`
WHERE t.`date` IS NULL
ORDER BY i.ID,d.`date`;
I now understand your requirement of dates being taken from the table; you want to find any gaps in the date ranges for each id.
This does what you need, but can probably be improved. Explanation below and you can view a working example.
DROP TABLE IF EXISTS Table1;
DROP TABLE IF EXISTS Year_Month_Calendar;
CREATE TABLE Table1 (
id INTEGER
,date CHAR(7)
,value CHAR(4)
);
INSERT INTO Table1
VALUES
(1,'2015-01','val1')
,(1,'2015-02','val2')
,(2,'2015-01','val1')
,(2,'2015-03','val1');
CREATE TABLE Year_Month_Calendar (
date CHAR(10)
);
INSERT INTO Year_Month_Calendar
VALUES
('2015-01')
,('2015-02')
,('2015-03');
SELECT ID_Year_Month.id, ID_Year_Month.date, Table1.id, Table1.date
FROM (
SELECT Distinct_ID.id, Year_Month_Calendar.date
FROM Year_Month_Calendar
CROSS JOIN
( SELECT DISTINCT id FROM Table1 ) AS Distinct_ID
WHERE Year_Month_Calendar.date >= (SELECT MIN(date) FROM Table1 WHERE id=Distinct_ID.ID)
AND Year_Month_Calendar.date <= (SELECT MAX(date) FROM Table1 WHERE id=Distinct_ID.ID)
) AS ID_Year_Month
LEFT JOIN Table1
ON ID_Year_Month.id = Table1.id AND ID_Year_Month.date = Table1.date
-- WHERE Table1.id IS NULL
ORDER BY ID_Year_Month.id, ID_Year_Month.date
Explanation
You need a calendar table which contains all dates (year/months) to cover the data you are querying.
CREATE TABLE Year_Month_Calendar (
date CHAR(10)
);
INSERT INTO Year_Month_Calendar
VALUES
('2015-01')
,('2015-02')
,('2015-03');
The inner select creates a table with all dates between the min and max date for each id.
SELECT Distinct_ID.id, Year_Month_Calendar.date
FROM Year_Month_Calendar
CROSS JOIN
( SELECT DISTINCT id FROM Table1 ) AS Distinct_ID
WHERE Year_Month_Calendar.date >= (SELECT MIN(date) FROM Table1 WHERE id=Distinct_ID.ID)
AND Year_Month_Calendar.date <= (SELECT MAX(date) FROM Table1 WHERE id=Distinct_ID.ID)
This is then LEFT JOINED to the original table to find the missing rows.
If you only want to return the missing row (my query displays the whole table to show how it works), add a WHERE clause to restrict the output to those rows where an id and date is not returned from Table1
Original answer before comments
You can do this without a tally table, since you say
Date range is: select distinct date from Table1
I've slightly changed the field names to avoid reserved words in SQL.
SELECT id_table.ID, date_table.`year_month`, table1.val
FROM (SELECT DISTINCT ID FROM table1) AS id_table
CROSS JOIN
(SELECT DISTINCT `year_month` FROM table1) AS date_table
LEFT JOIN table1
ON table1.ID=id_table.ID AND table1.`year_month` = date_table.`year_month`
ORDER BY id_table.ID
I've not filtered the results, in order to show how the query is working. To return the rows where only where a date is missing, add WHERE table1.year_month IS NULL to the outer query.
SQL Fiddle
You will need a tally table(s) or month/year tables. So you can then generate all of the potential combinations you want to test with. As far as exactly how to use it your example could use some expanding on such as last 12 months, last3 months, etc. but here is an example that might help you understand what you are looking for:
http://rextester.com/ZDQS5259
CREATE TABLE IF NOT EXISTS Tbl (
ID INTEGER
,Date VARCHAR(10)
,Value VARCHAR(10)
);
INSERT INTO Tbl VALUES
(1,'2015-01','val1')
,(1,'2015-02','val2')
,(2,'2015-01','val1')
,(2,'2015-03','val1');
SELECT yr.YearNumber, mn.MonthNumber, i.Id
FROM
(
SELECT 2016 as YearNumber
UNION SELECT 2015
) yr
CROSS JOIN (
SELECT 1 MonthNumber
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 10
UNION SELECT 11
UNION SELECT 12
) mn
CROSS JOIN (
SELECT DISTINCT ID
FROM
Tbl
) i
LEFT JOIN Tbl t
ON yr.YearNumber = CAST(LEFT(t.Date,4) as UNSIGNED)
AND mn.MonthNumber = CAST(RIGHT(t.Date,2) AS UNSIGNED)
AND i.ID = t.ID
WHERE
t.ID IS NULL
The basic idea to determine what you don't know is to generate all possible combinations of something could be. E.g. Year X Month X DISTINCT Id and then join back to figure out what is missing.
Probably not the prettiest but this should work.
select distinct c.ID, c.Date, d.Value
from (select a.ID, b.Date
from (select distinct ID from Table1) as a, (select distinct Date from Table1) as b) as c
left outer join Table1 d on (c.ID = d.ID and c.Date = d.Date)
where d.Value is NULL
We can use GREATEST to get greatest value from multiple columns like below
SELECT GREATEST(mark1,mark2,mark3,mark4,mark5) AS best_mark FROM marks
But now I want to get two best marks from all(5) marks.
Can I do this on mysql query?
Table structure (I know it is wrong - created by someone):
student_id | Name | mark1 | mark2 | mark3 | mark4 | mark5
This is not the most elegant solution but if you cannot alter the table structure then you can unpivot the data and then apply a user defined variable to get a row number for each student_id. The code will be similar to the following:
select student_id, name, col, data
from
(
SELECT student_id, name, col,
data,
#rn:=case when student_id = #prev then #rn else 0 end +1 rn,
#prev:=student_id
FROM
(
SELECT student_id, name, col,
#rn,
#prev,
CASE s.col
WHEN 'mark1' THEN mark1
WHEN 'mark2' THEN mark2
WHEN 'mark3' THEN mark3
WHEN 'mark4' THEN mark4
WHEN 'mark5' THEN mark5
END AS DATA
FROM marks
CROSS JOIN
(
SELECT 'mark1' AS col UNION ALL
SELECT 'mark2' UNION ALL
SELECT 'mark3' UNION ALL
SELECT 'mark4' UNION ALL
SELECT 'mark5'
) s
cross join (select #rn := 0, #prev:=0) c
) s
order by student_id, data desc
) d
where rn <= 2
order by student_id, data desc;
See SQL Fiddle with Demo. This will return the top 2 marks per student_id. The inner subquery is performing a similar function as using a UNION ALL to unpivot but you are not querying against the table multiple times to get the result.
I think you should change your database structure, because having that many marks horizontally (i.e. as fields/columns) already means you're doing something wrong.
Instead put all your marks in a separate table where you create a many to many relationship and then perform the necessary SELECT together with LIMIT.
Suggestions:
Create a table that you call mark_types. Columns: id, mark_type. I
see that you currently have 5 type of marks; it would be very simple
to add additional types.
Change your marks table to hold 3 columns: id,
mark/grade/value, mark_type (this column foreign constraints to
mark_types).
Write your SELECT query with the help of joins, and GROUP BY mark_type.
you can create a temporary table and then
Create a temporary table in a SELECT statement without a separate CREATE TABLE
query that table as follows
SELECT TOP 2 * FROM temp
ORDER BY mark DESC
then
drop temp table
Okay here's a new answer that's should work with the current table structure:
SELECT `student_id`, `Name`, `mark` FROM (SELECT `student_id`, `Name`, `mark1` AS `mark` FROM `marks`
UNION ALL
SELECT `student_id`, `Name`, `mark2` AS `mark` FROM `marks`
UNION ALL
SELECT `student_id`, `Name`, `mark3` AS `mark` FROM `marks`
UNION ALL
SELECT `student_id`, `Name`, `mark4` AS `mark` FROM `marks`
UNION ALL
SELECT `student_id`, `Name`, `mark5` AS `mark` FROM `marks`) AS `marks`
ORDER BY `mark` DESC
LIMIT 2