I have a table biblek2 items with those 4 columns :
id (autoincrement)
catid(int)
introtext(varchar)
ordering(int)
Table biblek2_items
╔════╦═══════╦═══════════╦══════════╗
║ ID ║ catid ║ introtext ║ ordering ║
╠════╬═══════╬═══════════╬══════════╣
║ 1 ║ 3024 ║ orange ║ 122 ║
║ 2 ║ 2024 ║ zebra ║ 45 ║
║ 3 ║ 3010 ║ juice ║ 55 ║
║ 4 ║ 3002 ║ build ║ 17 ║
║ 5 ║ 2003 ║ car ║ 87 ║
║ 6 ║ 1610 ║ other ║ 1521 ║
║ 7 ║ 1620 ║ other ║ 200 ║
╚════╩═══════╩═══════════╩══════════╝
I expect that
Table biblek2_items
╔════╦═══════╦═══════════╦══════════╗
║ ID ║ catid ║ introtext ║ ordering ║
╠════╬═══════╬═══════════╬══════════╣
║ 5 ║ 2003 ║ car ║ 1 ║
║ 4 ║ 3002 ║ build ║ 2 ║
║ 3 ║ 3010 ║ juice ║ 3 ║
║ 1 ║ 3024 ║ orange ║ 4 ║
║ 2 ║ 2024 ║ zebra ║ 5 ║
╚════╩═══════╩═══════════╩══════════╝
I want to
select * from biblek2_items where catid between 2001 and 3024
ORDER BY introtext ASC
empty the ordering column
reorder the ordering column by increment from 1 to n according to the result of the order column
I tried this with no success
DECLARE #variable int
SET #variable = 0
UPDATE `biblek2_items`
SET #variable = ordering = #variable + 1
WHERE ordering IN (SELECT ordering
FROM `biblek2_items`
WHERE catid BETWEEN 2001 AND 3024
ORDER BY `introtext` DESC)
I read in the forum that MySQL can't allow subrequests with ORDER BY, so could you help me
As explained in the comments :
The ORDER BY in your sub query makes no sense anyway, because, you don't LIMIT. So all rows will be returned and it doesn't matter how they are ordered because all of them are taken into account with the IN in your main query.
But there are other issues with your query.
Do this instead :
SET #row_number = 0 ;
UPDATE biblek2_items,
(select id, catid,introtext,ordering, (#row_number:=#row_number + 1) AS newordering
from biblek2_items
where catid between 2001 and 3024
ORDER BY introtext ASC
) as temp
SET biblek2_items.ordering = temp.newordering
WHERE biblek2_items.ID = temp.ID
Additionally, if you have a large table, and a lot of users actively writing on it, to avoid inconsistencies or locking issues, I would suggest a slightly different method, using a temporary table to store the computed new ordering.
CREATE TABLE biblek2_items_TEMP (ID INT, ordering INT);
SET #row_number = 0 ;
INSERT INTO biblek2_items_TEMP
select id, (#row_number:=#row_number + 1) AS newordering
from biblek2_items
where catid between 2001 and 3024
ORDER BY introtext ASC
;
UPDATE biblek2_items, biblek2_items_TEMP
SET biblek2_items.ordering = biblek2_items_TEMP.ordering
WHERE biblek2_items.ID = biblek2_items_TEMP.ID;
DROP TABLE biblek2_items_TEMP;
Tested successfully on MySQL 5.7 and MariaDB 10
Related
I have the following data:
╔════╦═══════╦═══════╗
║ id ║ group ║ place ║
╠════╬═══════╬═══════╣
║ 1 ║ 1 ║ a ║
║ 2 ║ 1 ║ b ║
║ 3 ║ 1 ║ b ║
║ 4 ║ 1 ║ a ║
║ 5 ║ 1 ║ c ║
║ 6 ║ 2 ║ a ║
║ 7 ║ 2 ║ b ║
║ 8 ║ 2 ║ c ║
╚════╩═══════╩═══════╝
How can I get the path of each group in MySQL?
The expected result is:
╔═══════╦════════════╗
║ group ║ path ║
╠═══════╬════════════╣
║ 1 ║ a-b-a-c ║
║ 2 ║ a-b-c ║
╚═══════╩════════════╝
Assuming that the end goal is to sort by group and id, and then simplify each group's sequence so that consecutive repeated places are only shown once:
Start by determining, for each row, whether the place or the group have changed since the previous row. There's a good solution to this problem in this answer.
Then use GROUP_CONCAT to merge the places together into a path.
Be aware that GROUP_CONCAT has a user-configurable maximum length, which by default is 1,024 characters.
SELECT
`group`,
GROUP_CONCAT(place ORDER BY id SEPARATOR '-') path
FROM
(SELECT
COALESCE(#place != place OR #group != `group`, 1) changed,
id,
#group:=`group` `group`,
#place:=place place
FROM
place_table, (SELECT #place:=NULL, #group:=NULL) s
ORDER BY `group`, id) t
WHERE
changed = 1
GROUP BY `group`;
I've inherited a database that includes a lookup table to find other patents that are related to a given patent.
So it looks like
╔════╦═══════════╦════════════╗
║ id ║ patent_id ║ related_id ║
╠════╬═══════════╬════════════╣
║ 1 ║ 1 ║ 2 ║
║ 2 ║ 1 ║ 3 ║
║ 3 ║ 2 ║ 1 ║
║ 4 ║ 2 ║ 3 ║
║ 5 ║ 3 ║ 2 ║
╚════╩═══════════╩════════════╝
And I want to filter out the reciprocal relationships. 1->2 and 2->1 are the same for my purposes so I only want 1->2.
I don't need to make the edit in the table, I just need a query the returns a list of the unique relationships, and while I'm sure it's simple I've been banging my head against the keyboard for far too long.
Here is a clever query which you can try using. The general strategy is to identify the unwanted duplicate records and then subtract them away from the entire set.
SELECT t.id, t.patent_id, t.related_id
FROM t LEFT JOIN
(
SELECT t1.patent_id AS t1_patent_id, t1.related_id AS t1_related_id
FROM t t1 LEFT JOIN t t2
ON t1.related_id = t2.patent_id
WHERE t1.patent_id = t2.related_id AND t1.patent_id > t1.related_id
) t3
ON t.patent_id = t3.t1_patent_id AND t.related_id = t3.t1_related_id
WHERE t3.t1_patent_id IS NULL
Here is the inner temporary table generated by this query. You can convince yourself that by applying the logic in the WHERE clause you will select the correct records. Non-duplicate records are characterized by t1.patent_id != t2.related_id, and all these records are retained. In the case of duplicates (t1.patent_id = t2.related_id), the record chosen from each pair of duplicates is the one where patent_id < related_id, as you requested in your question.
╔════╦══════════════╦═══════════════╦══════════════╦═══════════════╗
║ id ║ t1.patent_id ║ t1.related_id ║ t2.patent_id ║ t2.related_id ║
╠════╬══════════════╬═══════════════╬══════════════╬═══════════════╣
║ 1 ║ 1 ║ 2 ║ 2 ║ 1 ║ * duplicate
║ 1 ║ 1 ║ 2 ║ 2 ║ 3 ║
║ 2 ║ 1 ║ 3 ║ 3 ║ 2 ║
║ 3 ║ 2 ║ 1 ║ 1 ║ 2 ║ * duplicate
║ 3 ║ 2 ║ 1 ║ 1 ║ 3 ║
║ 4 ║ 2 ║ 3 ║ 3 ║ 2 ║ * duplicate
║ 5 ║ 3 ║ 2 ║ 2 ║ 1 ║
║ 5 ║ 3 ║ 2 ║ 2 ║ 3 ║ * duplicate
╚════╩══════════════╩═══════════════╩══════════════╩═══════════════╝
Click the link below for a running example of this query.
SQLFiddle
Try something like
select distinct * from
(select patient_id, related_id from TABLENAME
union
select related_id, patient_id from TABLENAME
);
Okay you're right the above won't work. Try
select patient_id, related_id from TABLENAME p1
where p1.patiend_id not in
(select patient_id from TABLENAME p2
where p2.related_id = p1.related_id)
I'd like to merge rows based on multiple criteria, essentially removing duplicates where I get to define what "duplicate" means. Here is an example table:
╔═════╦═══════╦═════╦═══════╗
║ id* ║ name ║ age ║ grade ║
╠═════╬═══════╬═════╬═══════╣
║ 1 ║ John ║ 11 ║ 5 ║
║ 2 ║ John ║ 11 ║ 5 ║
║ 3 ║ John ║ 11 ║ 6 ║
║ 4 ║ Sam ║ 14 ║ 7 ║
║ 5 ║ Sam ║ 14 ║ 7 ║
╚═════╩═══════╩═════╩═══════╝
In my example, let's say I want to merge on name and age but ignore grade. The result should be:
╔═════╦═══════╦═════╦═══════╗
║ id* ║ name ║ age ║ grade ║
╠═════╬═══════╬═════╬═══════╣
║ 1 ║ John ║ 11 ║ 5 ║
║ 3 ║ John ║ 11 ║ 6 ║
║ 4 ║ Sam ║ 14 ║ 7 ║
╚═════╩═══════╩═════╩═══════╝
I don't particularly care if the id column is updated to be incremental, but I suppose that would be nice.
Can I do this in MySQL?
My suggestion, based on my above comment.
SELECT distinct name, age, grade
into tempTable
from theTable
This will ignore the IDs and give you only a distinct dump, and into a new table.
Then you can either drop the old and, and rename the new one. Or truncate the old one, and dump this back in.
You could just delete the duplicates in place like this:
delete test
from test
inner join (
select name, age, grade, min(id) as minid, count(*)
from test
group by name, age, grade
having count(*) > 1
) main on test.id = main.minid;
Example: http://sqlfiddle.com/#!9/f1a38/1
Having a table named books which has the following structure:
╔════════════╦═══════════╦════════╗
║ LibraryId ║ BookId ║ Price ║
╠════════════╬═══════════╬════════╣
║ 123 ║ 9001 ║ 10.99 ║
║ 123 ║ 9005 ║ 12.99 ║
║ 123 ║ 9006 ║ 7.99 ║
║ 124 ║ 8012 ║ 6.49 ║
║ 124 ║ 9001 ║ 3.19 ║
║ 124 ║ 9076 ║ 7.39 ║
╚════════════╩═══════════╩════════╝
How could I do a select that would return the full table, but additionally a field named Ordinal, that "counts" the number of books per library. The result should look something like:
╔════════════╦═══════════╦════════╦════════╗
║ LibraryId ║ BookId ║ Price ║Ordinal ║
╠════════════╬═══════════╬════════╬════════╣
║ 123 ║ 9001 ║ 10.99 ║ 1 ║
║ 123 ║ 9005 ║ 12.99 ║ 2 ║
║ 123 ║ 9006 ║ 7.99 ║ 3 ║
║ 124 ║ 8012 ║ 6.49 ║ 1 ║
║ 124 ║ 9001 ║ 3.19 ║ 2 ║
║ 124 ║ 9076 ║ 7.39 ║ 3 ║
╚════════════╩═══════════╩════════╝════════╝
I have tried something like:
SET #var_record = 1;
SELECT *, (#var_record := #var_record + 1) AS Ordinal
FROM books;
But this will continue counting irrespective of the libraryId. I need something that will reset the ordinal every time the libraryId changes. I'd prefer a single query instead of procedures.
Test data sql scripts:
create temporary table books(libraryId int, bookId int, price double(4,2));
insert into books (libraryId, bookId, price) values (123, 9001, 10.99),(123, 9005, 10.99),(123, 9006, 10.99),(124, 8001, 10.99),(124, 9001, 10.99),(124, 9076, 10.99);
Using variables and conditions you can reset the counter based on a condition (libraryId has changed). Mandatory to order by the column libraryId.
SELECT books.*,
if( #libId = libraryId,
#var_record := #var_record + 1,
if(#var_record := 1 and #libId := libraryId, #var_record, #var_record)
) AS Ordinal
FROM books
JOIN (SELECT #var_record := 0, #libId := 0) tmp
ORDER BY libraryId;
The second if statement is used to group two assignments together and return #var_record.
if(#var_record := 1 and #libId := libraryId, #var_record, #var_record)
perhaps you can use aggregated functions
SELECT a.LibraryId, a.BookId, a.Price,
(SELECT COUNT(b.BookId) FROM books b WHERE b.BookId = a.BookId AND b.LibraryId = a.LibraryId) AS Ordinal
FROM a.books
╔════════╦═══════════╦═══════╗
║ MSG_ID ║ RANDOM_ID ║ MSG ║
╠════════╬═══════════╬═══════╣
║ 1 ║ 22 ║ apple ║
║ 2 ║ 22 ║ bag ║
║ 3 ║ 0 ║ cat ║
║ 4 ║ 0 ║ dog ║
║ 5 ║ 0 ║ egg ║
║ 6 ║ 21 ║ fish ║
║ 7 ║ 21 ║ hen ║
║ 8 ║ 20 ║ glass ║
╚════════╩═══════════╩═══════╝
Want to fetch 3 records in a lot such a way that all the data of a particular random_id is picked up .
Result Required:
║ MSG_ID ║ RANDOM_ID ║ MSG ║
╠════════╬═══════════╬═══════╣
║ 1 ║ 22 ║ apple ║
║ 2 ║ 22 ║ bag ║
║ 3 ║ 0 ║ cat ║
Current Result:
║ MSG_ID ║ RANDOM_ID ║ MSG ║
╠════════╬═══════════╬═══════╣
║ 1 ║ 22 ║ apple ║
║ 3 ║ 0 ║ cat ║
║ 4 ║ 0 ║ dog ║
______________________________
Query Used:
SELECT ID,Random_ID, GROUP_CONCAT(message SEPARATOR ' ' ),FLAG,mobile,sender_number,SMStype
FROM messagemaster
WHERE Random_ID > 0
GROUP BY Random_ID
UNION
SELECT ID,Random_ID, message,FLAG,mobile,sender_number,SMStype
FROM messagemaster
WHERE Random_ID = 0
order by random_id LIMIT 100;
I don't want to pick up records using group by.I want to fetch all the records w rt random_ids .Like , if there is a random_id for which there are 3 records and if the query has limit =3 , then i want all the data w r t those random_id to be picked up.
The situation is if I fetch rows with limit 100 , i dont want that some of the data with the random id present in the result set is not picked.
For example if i am picking records limit by 3 then for random id=22 , all records with random id =22 should be picked .
Consider the following...
SELECT b.*
FROM
( SELECT x.*, SUM(y.cnt)
FROM
( SELECT random_id,COUNT(*) cnt FROM messagemaster GROUP BY random_id) x
JOIN
( SELECT random_id,COUNT(*) cnt FROM messagemaster GROUP BY random_id) y
ON y.random_id >= x.random_id
GROUP
BY x.random_id
HAVING SUM(y.cnt) < 4
) a
JOIN messagemaster b
ON b.random_id = a.random_id;