Get record id from looped records - mysql

Here is the story
i have two mysql tables like :
master
id, descr
1, master 1
2, master 2
details
id, descr, masterid
1, test 1, 1
2, test 2, 1
3, test 3, 1
4, test 1, 2
5, test 2, 2
6, test 3, 2
I need a query to get the rows from the first table and a looped column from the details
For example :
first execute :
1,master 1, 1
2,master 2, 4
second execute
1,master 1, 2<- From the details table
2,master 2, 5<- From the details table
third execute
1,master 1, 3<- From the details table
2,master 2, 6<- From the details table
Fourth execute :
1,master 1, 1
2,master 2, 4
I guess i have to add a column on master table to keep the last selected record from the details table but i can't design the query.
Any ideas?
Thank you
Vangelis

The thing is - you can do it with SQL only. But that will be session-related. By that, I mean this query:
SELECT
master.*,
o.id AS oid
FROM
master
INNER JOIN
(SELECT
d.*,
#num:=IF(#id=masterid, #num:=#num+1, 1) AS num,
#id:=masterid
FROM
(SELECT * FROM details ORDER BY masterid) AS d
CROSS JOIN
(SELECT #id:=0, #num:=0) AS init
) AS o
ON master.id=o.masterid
INNER JOIN
(SELECT
masterid,
COUNT(id) AS c
FROM
details
GROUP BY
masterid) AS counts
ON master.id=counts.masterid
CROSS JOIN
(SELECT #RUN_COUNT:=IF(#RUN_COUNT IS NULL, 1, #RUN_COUNT+1)) AS r
WHERE
o.num=IF(!(#RUN_COUNT%(counts.c)), counts.c, #RUN_COUNT%(counts.c));
Very important detail of it is #RUN_COUNT. It will be looped only in terms of one session. That is: if you'll start another session, then initializing of this variable will happen again:
(SELECT #RUN_COUNT:=IF(#RUN_COUNT IS NULL, 1, #RUN_COUNT+1)) AS r
-as you can see, this simply checks if it isn't initialized - and, if it's not, then it will initialize it. So, this will fail if you're acting in different sessions.
SQLFiddle behaves like one session, thus it's working there (but you'll need first to set #RUN_COUNT to NULL, of course - because I've already run this query)
The query above will work if there are different counts for different master ids (like I've done in fiddle, 4 for masterid=1 and 3 for masterid=2). But - again, you can not rely on #RUN_COUNT in common case. If your session isn't persistent between your application and your DBMS - then you'll have to handle it in application.

in reference to what I said earlier --- if you were to do it in php, this is the sql select you would want to do to get the data in the correct format.. from here you could do a loop inside a loop in php with a limit set to break at 2.. should probably do the trick
SELECT
m.id AS master_id,
m.descr,
d.id AS detail_id
FROM master m
JOIN details d ON d.masterid = m.id

Related

MySQL counter for specific situations

I need to generate unique "ids", the catch is, it can be only between 1 - 99999.
The "good" thing is, that it has to be unique only in group with another column.
We have groups, each group has its own "group_id" and each group need something like unique('group_id', 'increment_id')
The 99999 records is enough for several years for each group right now, but it is not enough for all groups together, therefore I cant just create table with AUTO_INCREMENT and inserting there records and taking its auto increment.
For example, if I need 5 records for Group one and three records for Group two, I suppose to get something like this:
group_id, increment_id
1, 1,
1, 2,
1, 3,
1, 4,
1, 5,
2, 1
2, 2,
2, 3
Also, the conflict is not an option, therefore using something like "length" can be tricky, if done programatically (there can be i.e. 10 requests at once, each of them first select length for given group_id and then tries to create 10 rows with same increment_id)
However I am thinking - if I set it up as the value of subselect of count, than it will always be "ok"?
You can create a auxiliar table named counters to manage that:
table: counters
columns: group_id, current_counter
OR
Each time you insert a row increment_id = select max(increment_id)+1 from table_xxx where group_id = group_xxxx
You can use user variables to get the incrementing number within each group_id:
select
t.*,
#rn := if(#group_id = group_id,
#rn + 1,
if(#group_id := group_id, 1, 1)
) increment_id
from (
select group_id
from your_table t
/* some where clauses */
order by group_id
) t
cross join (
select #rn := 0,
#group_id := - 1
) t2

Use subquery in mysql

The query below gives me 2 out of the 3 answers I'm looking for. On the sub-query select I get null instead of no
the 3 possible values for column name isCyl could be blank, yes, no
I'm not sure if the sub-query is the best way to go about it, but I don't know how else to re-state the query.
The schedule table has a series of columns to show what tasks must be completed on an assignment. Related tables store the results of the tasks if they were assigned to be completed. So I need to test if a specific task was scheduled. If so, then I need to see if the results of the task have been recorded in the related table. For brevity I am only showing one of the columns here.
SELECT s.`reckey`,
if(s.cylinders="T",
(select
if(c.areckey is not null,
"yes",
"no"
)
from cylinders c where c.areckey = s.reckey limit 1
)
,""
) as isCyl
from schedule s
where s.assignmentDate between 20161015 and 20161016
order by s.reckey
Use a LEFT JOIN, which returns NULL for columns in the child table when there's no match.
SELECT s.reckey, IF(s.cylinders = "T",
IF(c.areckey IS NOT NULL, 'yes', 'no'),
"") AS isCyl
FROM schedule AS s
LEFT JOIN cylinders AS c ON c.areckey = s.reckey
WHERE s.assignmentDate between 20161015 and 20161016
ORDER BY s.reckey
If there can be multiple rows in cylinders with the same areckey, change it to:
LEFT JOIN (select distinct areckey FROM cylinders) AS c on c.areckey = s.reckey
or use SELECT DISTINCT in the main query.

Nested SELECT with IN [duplicate]

This question already has answers here:
SQL NOT IN not working
(4 answers)
Closed 8 years ago.
I am using Mysql and trying to migrate data from one DB to other. I have not worked on databases earlier.
This query gives me close to 300 results
select distinct internal_id from ratingsapp.hotel03
But this one returns no results and has no errors either:
select restname from City.resturant where restid not in
(select distinct internal_id from ratingsapp.hotel03)
Now if I manually pick few internal_ids from the hotel03 table, and put this in place of the nested query, the query returns proper results. I am not sure what exactly am I doing wrong.
This usually happens when one of the values is NULL. So this might work:
select restname
from City.resturant
where restid not in (select distinct internal_id from ratingsapp.hotel03 where internal_id is not null);
Another way to write this query is with not exists:
select restname
from City.resturant r
where not exists (select 1
from ratingsapp.hotel03 h
where h.internal_id = r.restid
);
The way this works, NULL is handled correctly without directly checking for it. That is one reason why NOT EXISTS is preferable to NOT IN.
Are you sure it is not because all the restid from City.restaurant are in internal_id of ratingsapp.hotel03 ? You say that you manually pick a few of these ids and there was a result, but check this:
distinct City.restaurant.restid: 1, 2, 3, 4, 5
distinct ratingsapp.hotel03.internal_id: 1, 2, 3, 4, 5
Then your query will return nothing, as all the restid are not not in the internal_id. But if you pick a few ids from ratingsapp.hotel03.internal_id, for instance:
select restname
from City.resturant
where restid not in (1, 2, 3)
Then you will have all the City.restaurant with a restid of 4 or 5!

Select JOINed substring

Is there some way to JOIN on a resulting substring?
I've got a query that looks like this:
SELECT _atc_codes.se, _atc_codes.code, SUBSTR(_atc_codes.code, 1, 1)
FROM diagnoses
JOIN _atc_codes
ON (_atc_codes.id = diagnoses.atc_code)
Now I want to add an extra column to this query which should be SUBSTR(_atc_codes.code, 1, 1) joined to its corresponding _atc_codes.se, how do I do that?
This image shows incorrect results, the 4th column should read "Matsmältningsorgan och ämnesomsättning" (corresponding cell in _atc_codes.se).
SQL Fidde: http://www.sqlfiddle.com/#!2/6695d
Can you try (this example uses MySQL user defined variables, it is important for this query that MySQL knows to use a value outside from subquery for comparision)
SELECT _atc_codes.se, _atc_codes.code,
#code_substr:=SUBSTR(_atc_codes.code, 1, 1) AS code_substr,
(
SELECT se
FROM _atc_codes
WHERE code=#code_substr
LIMIT 1
) AS code_substr_se
FROM diagnoses
JOIN _atc_codes ON _atc_codes.id = diagnoses.atc_code
Or (this example assigns table alias to outer table which is used in subquery because you are using a table twice and MySQL does not know which table to reference in SUBSTR in subquery):
SELECT outer_codes .se, outer_codes .code,
SUBSTR(outer_codes .code, 1, 1) AS code_substr,
(
SELECT se
FROM _atc_codes
WHERE code=SUBSTR(outer_codes.code, 1, 1)
LIMIT 1
) AS code_substr_se
FROM diagnoses
JOIN _atc_codes AS outer_codes ON outer_codes.id = diagnoses.atc_code
A third way would be adding a second JOIN and then group resultset like
SELECT _atc_codes_1st.se, _atc_codes_1st.code,
SUBSTR(_atc_codes_1st.code, 1, 1) AS code_substr,
MAX(_atc_codes_2nd.se) AS code_substr_se
FROM diagnoses
JOIN _atc_codes AS _atc_codes_1st ON _atc_codes_1st.id = diagnoses.atc_code
JOIN _atc_codes AS _atc_codes_2nd ON _atc_codes_2nd.code = SUBSTR(_atc_codes_1st.code, 1, 1)
GROUP BY _atc_codes_1st.se, _atc_codes_1st.code,code_substr
Deciding which variant to use, it would be the best to add a EXPLAIN to your query to show execution plan. Good luck.

MySQL order by IN order

i have simple query:
SELECT data FROM table WHERE id IN (5, 2, 8, 1, 10)
Question is, how can i select my data and order it like in my IN.
Order must be 5, 2, 8, 1, 10.
Problem is that i have no key for order. IN data is from other query (1), but i need to safe order.
Any solutions?
(1)
SELECT login
FROM posts
LEFT JOIN users ON posts.post_id=users.id
WHERE posts.post_n IN (
2280219,2372244, 2345146, 2374106, 2375952, 2375320, 2371611, 2360673, 2339976, 2331440, 2279494, 2329266, 2271919, 1672114, 2301856
)
Thanx for helping, solutions works but very slow, maybe find something better later, thanx anyway
The only way I can think to order by an arbitrary list would be to ORDER BY comparisons to each item in that list. It's ugly, but it will work. You may be better off sorting in whatever code you are doing the selection.
SELECT data FROM t1 WHERE id IN (5, 2, 8, 1, 10)
ORDER BY id = 10, id = 1, id = 8, id = 2, id = 5
The order is reversed because otherwise you would have to add DESC to each condition.
You can use a CASE statement
SELECT data
FROM table WHERE id IN (5, 2, 8, 1, 10)
ORDER BY CASE WHEN id = 5 THEN 1 WHEN id = 2 THEN 2 WHEN id = 8 THEN 3 WHEN id = 1 THEN 4 WHEN id = 10 THEN 5 END
SELECT data FROM table
WHERE id IN (5, 2, 8, 1, 10)
ORDER BY FIELD (id, 5, 2, 8, 1, 10)
http://dev.mysql.com/doc/refman/5.5/en/string-functions.html#function_field
Might be easier to auto-generate (because it basically just needs inserting the wanted IDs comma-separated in the same order a second time) than the other solutions suggested using CASE or a number of ID=x, ID=y ...
http://sqlfiddle.com/#!2/40b299/6
I think that's what you're looking for :D Adapt it to your own situation.
To do this dynamically, and within MySql, I would suggest to do the following:
Create a temp table or table variable (not sure if MySql has these), with two columns:
OrderID mediumint not null auto_increment
InValue mediumint -(or whatever type it is)
Insert the values of the IN clause in order, which will generate ID's in order of insertion
Add a JOIN to your query on this temp table
Change your Order By to be
order by TempTable.OrderID
Drop temp table (again, in SQL inside a stored proc, this is automatic, not sure about MySql so mentioning here for full disclosure)
This effectively circumvents the issue of you not having a key to order by in your table ... you create one. Should work.