looking for a field name that contains specific data (mySql) - mysql

i got table named "teachers_db" with field names:
teacher_name
instrument1
instrument2
payment1
payment2
In my program i get list of teachers and what instrument they are teaching.
e.i:
Mathew - piano
Mathew - drums
Adam - guitar
I want to phrase a code line in mySQL that get a teacher_name and instrument (instrument1 = "piano" or instrumnet2 = "drums" and so on), and return the payment for it (payment1 if its a piano, playment2 if its a drums).
i'm hammering my head with this problem, please help :)

One way to get the result is to use two separate queries, combined with a UNION ALL operator. For example:
SELECT q.teacher_name
, q.instrument1 AS instrument
, q.payment1 AS payment
FROM teachers_db q
WHERE q.instrument1 <> ''
UNION ALL
SELECT r.teacher_name
, r.instrument2 AS instrument
, r.payment2 AS payment
WHERE r.instrument2 <> ''
ORDER BY 1,2
Another approach:
SELECT t.teacher_name
, CASE i.n WHEN 1 THEN t.instrument1 WHEN 2 THEN t.instrument2 END AS instrument
, CASE i.n WHEN 1 THEN t.payment1 WHEN 2 THEN t.payment2 END AS payment
FROM (SELECT 1 AS n UNION ALL SELECT 2) i
CROSS
JOIN teachers_db t
HAVING instrument <> ''
ORDER BY 1,2

Related

SQL - Get rows where all columns are in a list that have the same value in another column

I have a database as follows:
drinks_id ingredients_master_id
1 2
1 3
1 4
2 2
2 4
3 5
And I'm looking for a query where I can give it a list of ingredients_master_id such as 2,4 and it returns all of the drinks_id's that have exactly 2,4.
So in this case if I gave it ingredients_master_id 2,4,5 it would return drinks_id 2 and 3. And if I gave it 5 it would return drinks_id 3.
This is what I have so far but it's currently not displaying the correct info.
SELECT DISTINCT drinks.id
FROM drinks
WHERE drinks.id NOT IN
(
SELECT drinks.id
FROM ingredients
JOIN drinks ON ingredients.drinks_id = drinks.id
WHERE ingredients.ingredients_master_id NOT IN
(
2,3,4,5,6
)
);
You probably achieve the desired result using not exists as follows:
Select t.*
From your_table t
Where t.ingredients_master_id in (2,4,5)
And not exists
(Select 1 from your_table tt
Where tt.drinks_id = t.drinks_id
And tt.ingredients_master_id not in (2,4,5))
And I'm looking for a query where I can give it a list of ingredients_master_id such as 2,4 and it returns all of the drinks_id's that have exactly 2,4.
You can use group by and having:
select drinks_id
from t
group by drinks_id
having sum( ingredients_master_id in (2, 4) ) = 2;
The "= 2" is the size of the list, so you need to adjust that for different lists.
You can use as below:
select drink_id
from (
select drink_id,listagg(ingredients_master_id,',') within group( order by ingredients_master_id) val from <Table> group by drink_id)
where case
when instr('**2,4,5**',val)> 0
Then 'Y'
else 'N'
end = 'Y';

How to get sorted result within INNER JOIN on joined table

I'm trying to get results from a join of 2 tables. Typically, not an issue, but I'm banging my head on this one. Basically, table1 (sales_dealers) is joined with table2 (sales_dealerEvents). Normally, easy-peasy. The problem is that I want to only get records from table2 if there are only 2 events (why I added the HAVING). But I need to get the value of the second event. With my query now, I can only get the value of the first event. It's like I need an ORDER BY EventID DESC on the joined table. It giving me ASC by default with this query.
Here is my query:
SELECT e.EventAction
, d.DealerID
, d.AgentID
, d.DealerName
, d.Email
, SUBSTRING_INDEX(d.PrimaryContactName, ' ', 1) AS FirstName
, d.State
, COUNT(e.EventID) AS EventCount
FROM sales_dealers d
JOIN sales_dealerEvents e
ON d.DealerID = e.DealerID
WHERE d.AgentID = 1
AND d.StatusID = 3
AND d.Email REGEXP '^[a-zA-Z0-9][a-zA-Z0-9._-]*#[a-zA-Z0-9][a-zA-Z0-9._-]*\\.[a-zA-Z]{2,4}$'
GROUP
BY d.DealerID
HAVING COUNT(e.EventID) = 2
ORDER
BY e.EventID DESC
Here is my Schema for the two tables (mysql version 5.7.27 )
sales_dealers
------------------
DealerID (int PK)
StatusID (int)
AgentID (int)
DealerName (varchar)
Email (varchar)
PrimaryContactName (varchar)
State (varchar)
sales_dealerEvents
------------------
EventID (int PK)
DealerID (FK)
AgentID (FK)
EventDateTime (datetime)
EventAction (longtext)
Here is sample data. Currently, the above query returns this:
Prospect Entry 784 1 Dealer Name One sales#dealer.com CA 2
Prospect Entry 782 1 Some other dealer hello#dealer.com MT 2
Prospect Entry 781 1 Dealer Store contact#dealer.com OK 2
I would like it to return this:
Initial Contact 784 1 Dealer One sales#dealer.com CA 2
Initial Contact 782 1 Some other dealer hello#dealer.com MT 2
Initial Contact 781 1 Dealer Store contact#dealer.com OK 2
Here is the same sample data of but showing the relationship of the one to many in sales_dealerEvents
sales_dealer
784 Dealer Name One
sales_dealerEvents
1000 784 1 Prospect Entry 2020-06-02 01:00:00
1010 784 1 Initial Contact 2020-07-03 01:00:00
I'm feeling I might have to do this in a subquery to sort the event so the second event is first. Not sure. I might also need to match criteria of that second event too.
UPDATE - Here is my SQL fiddle
http://sqlfiddle.com/#!9/ef6f2c/2
As you can see with the fiddle.. all event records returned are 'prospect entry'.. which were the first entries for 'events' for those. Each have 2. The second is 'Initial Contact'. Their date and EventID is after the 'prospect entry'.
Hope the below query helps and you can see the demo here: http://sqlfiddle.com/#!9/ef6f2c/37/0
SELECT
EventAction,
DealerID,
AgentID,
DealerName,
Email,
FirstName,
State,
EventID
from
(
SELECT
e.EventAction,
d.DealerID,
d.AgentID,
d.DealerName,
d.Email,
SUBSTRING_INDEX(d.PrimaryContactName, ' ', 1) AS FirstName,
d.State,
e.EventID as EventID
FROM
sales_dealers d
JOIN
sales_dealerEvents e
ON d.DealerID = e.DealerID
WHERE
d.AgentID = 1
AND d.StatusID = 3
AND d.Email REGEXP '^[a-zA-Z0-9][a-zA-Z0-9._-]*#[a-zA-Z0-9][a-zA-Z0-9._-]*\\.[a-zA-Z]{2,4}$'
ORDER BY
e.EventID DESC
)
a
GROUP BY
DealerID
HAVING
COUNT(EventID) = 2
This sounds like you want window functions:
select . . .
from sales_dealers d join
(select e.*,
row_number() over (partition by e.DealerID order by e.EventDateTime) as seqnum
from sales_dealerEvents e
) e
on e.DealerID = d.DealerID and e.seqnum = 2
where . . .
Note this gets data from the second event even if there are more than two events. That seems consistent with the question, but it is easy enough to use count(*) as a window function to do further filtering.

How to select the keyword in a string list in MySQL?

I have two tables called tb_student (id,name,age,sex) and tb_course (id,namelist,title).
For example, tb_student data is:
id name age sex
1 Tommy 11 1
2 Marry 11 0
3 Mike 11 1
4 Lucy 10 0
And tb_course data is:
id namelist title
1 Tommy,Lucy math
2 Marry,Tommy English
Now if I want to get the student who selected math, I could write the sql as:
select * from tb_student where
FIND_IN_SET(name,(select namelist from tb_course where title='math'));
It is OK, it returns 'Tommy' and 'Lucy'.
But now if I have a student called Tom and he doesn't select math, my sql returns Tom as well.
How to change my code? Do I need to define a function to loop the namelist?
best way is normalize your data. but you can do a litle trick appending , to solve this.
this way instead of TOM you search for ,TOM,
SELECT tb_student.*
FROM tb_student
JOIN tb_course
ON concat(',', namelist, ',') LIKE
concat('%,', NAME, ',%') ;
You can try FIND_IN_SET with INNER JOIN.
SELECT tb_student.*
FROM
tb_student
INNER JOIN tb_course ON FIND_IN_SET(tb_student.`name`,tb_course.`namelist`) >0
WHERE tb_course.`title` = 'math'
Hope this helps.

Select From table name obtained dynamically from the query

I have 3 Tables
campaign1 (TABLE)
id campaign_details
1 'some detail'
campaign2 (TABLE)
id campaign_details
1 'some other detail'
campaign_list (TABLE)
id campaign_table_name
1 'campaign1'
2 'campaign2'
Campaign list table contains the table name of the two tables described above. I want to Select from the Campaign List table and get the record count using the table name i get from this select
For eg.
using select i get campaign1(Table name). Then i run select query on campaign1 to count number of records.
What i'm doing right now is .
-Select from campign_list
-loop through all campaign_table_names and run select query individually
Is there a way to do this using a single query
something like this
select campaign_name,(SELECT COUNT(*) FROM c.campaign_name) as campcount from campaign_list c
SQLFiddle : http://sqlfiddle.com/#!9/b766d/2
It's not possible inside a single query to build it dynamically but it's possible to cheat. Especially if there are only two linked tables.
I've listed two options
left outer join both tables
select campaign_name,
coalesce(c1.campaign_details, c2.campaign_details)
from campaign_list c
left join campaign1 c1 using (id)
left join campaign2 c2 using (id);
union all two different selects
select campaign_name,
campaign_details
from campaign_list c
join campaign1 c1 using (id)
union all
select campaign_name,
campaign_details
from campaign_list c
join campaign2 c2 using (id);
sqlfiddle
Combine your campaign tables to 1 table and add an column named 'type' (int).
campaign_items tables:
item_id item_details item_type
1 'some detail' 1
2 'some detail' 1
3 'some other detail' 2
4 'some other detail' 2
campaign_lists table
campaign_id campaign_name
1 'campaign1'
2 'campaign2'
Then you can use the following select statement:
SELECT campaign_name, (SELECT COUNT(*) FROM campaign_items WHERE item_type = campaign_id) as campaign_count
FROM campaign_lists
Oops, writing took me so long that you got this answered by Colin Raaijmakers already. Well, I'll post my answer anyway in spite of being more or less the same answer. Maybe my elaboration helps you see the problem.
Your problem stems from a bad database design. A database is made to order data and its relations. A CD database holds albums, songs, artists, etc. A business database may hold items, warehouses, sales and so on. Your database holds table names. [... time for thinking :-) ]
(When writing a DBMS you would want to store table names, column names, constraints etc., but I guess I am right supposing that you are not writing a new DBMS.)
So create tables that deal with your actual data. E.g.:
campain_type (id_campain_type, description, ...)
campain (id_campain, id_campain_type, campain_date, ...)
campain_type
id_campain_type description
1 Type A
2 Type B
3 Type C
campain
id_campain id_campain_type date
33 1 2015-06-03
85 2 2015-10-23
97 2 2015-12-01
query
select
ct.description,
(select count(*) from campain c where c.id_campain_type = ct.id_campain_type) as cnt
from campain_type ct;
result
description cnt
Type A 1
Type B 2
Type C 0

MySQL - return rows in one table that correspond to minimum date in another (indirectly linked) table

Table semesters:
semesterID startDate
1 2013-01-01
2 2013-03-01
3 2013-06-01
Table classes:
classID class_title semesterID
1 Math 1
2 Science 1
3 Math 2
4 Science 2
5 Math 3
6 Science 3
Table persons:
personID firstName lastName
1 John Jones
2 Steve Smith
Table class_person:
classID personID
1 1
2 1
5 1
6 1
3 2
4 2
5 2
6 2
I need to get a list of all the people, with the first semester in which they took a class (semester with the oldest startDate).
firstName, lastName, semesterID, startDate
John Jones 1 2013-01-01
Steve Smith 2 2013-03-01
I've spent hours trying to figure this out. Here's the closest I've gotten (although it is not close at all!):
SELECT p.firstName, p.lastName, MIN(s.startDate) AS min_startDate
FROM semesters s
INNER JOIN classes c ON s.semesterID = c.semesterID
INNER JOIN class_person cp ON cp.classID = c.classID
INNER JOIN persons p ON p.personID = cp.personID
GROUP BY cs.personID
ORDER BY min_startDate, p.lastName, p.firstName
Any help would be massively appreciated. Thank you.
You could end up using a monster like the following (fiddle):
select persons.firstName, persons.lastName,
semesters.semesterID, semesters.startDate
from persons, semesters,
(select p.personID,
(select semesters.semesterID
from semesters, classes, class_person
where semesters.semesterID = classes.semesterID
and classes.classID = class_person.classID
and class_person.personID = p.personID
order by semesters.startDate
limit 1) as semesterID
from (select distinct personID from class_person) as p
) as ps
where persons.personID = ps.personID
and semesters.semesterID = ps.semesterID
The subquery p identifies all persons. For each, ps will contain a single row. Its personID is simply copied, its semesterID is computed by a subquery, which sorts semesters by date but returns the ID. The outermost query then re-adds the date.
If you don't really need the semesterID, you could avoid one layer. If your semesters are in order, i.e. their IDs have the same order as their startDates, then you could simply use a single query, much like your own, and return min(semesterID) and min(startDate).
On the whole, this question reminds me a lot of my own question, Select one value from a group based on order from other columns. Answers suggested there will likely apply here as well. In particular, there are approaches using user variables which I still don't feel comfortable about, but which will make this whole mess a lot easier and seem to work well enough. So adapting this answer, you get a query like this (fiddle):
SELECT p.firstName, p.lastName, s2.semesterID, s2.startDate
FROM persons p
INNER JOIN (
SELECT #rowNum:=IF(#personID=cp.personID,#rowNum+1,1) rowNum,
#personId:=cp.personID personID,
s.semesterID, s.startDate
FROM (SELECT #personID:=NULL,#rowNum:=0) dummy
INNER JOIN semesters s
INNER JOIN classes c ON s.semesterID = c.semesterID
INNER JOIN class_person cp ON cp.classID = c.classID
ORDER BY cp.personID, s.startDate
) s2 ON p.personID = s2.personID
WHERE s2.rowNum = 1
I'll leave adapting the other answers as an excercise.