Mysql Incrementing variable performance - mysql

I has looking for a way to sort items in a mysql table.
Here a simplified version of the table
sqlfiddle => http://sqlfiddle.com/#!2/78521b/3/0
CREATE TABLE IF NOT EXISTS `test_sort` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sort` int(11) NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `sort` (`sort`)
);
INSERT INTO `test_sort` (`id`,`sort`, `name`) VALUES
(1, 1, 'Joker'),
(2, 3, 'Queen of Spade'),
(3, 6, 'King of Heart'),
(4, 4, 'Ace of Diamond'),
(5, 17, 'Three of Clubs'),
(6, 60, 'Seven of Clubs'),
(7, 2, 'Ten of Spades'),
(8, 5, 'Ace of Heart');
So once the items (cards) has been sorted by the user i want to run the query on the sort column so it remains consistent.
Solution found here : MySQL update a field with an incrementing variable
SET #n=0;
UPDATE `test_sort` SET `sort` = #n := #n + 1 ORDER BY `sort`
QUESTION: how this query would act (performance wise) if it was used on thousands (or millions) of records?

Don't store sort in the table; store it in a separate table. Furthermore, don't UPDATE that table, recreate it. Further-furthermore, use the following to avoid any downtime:
CREATE TABLE New SELECT ... -- generate new sort order
RENAME TABLE Sort TO Old, New To Sort;
DROP TABLE Old;

Related

Get Rows contains Pattern MYSQL

I have very simple table like below
CREATE TABLE `tbl_data` (
`id` int(11) NOT NULL,
`won` tinyint(4) NOT NULL,
`time` datetime NOT NULL DEFAULT current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `tbl_data`
--
INSERT INTO `tbl_data` (`id`, `won`, `time`) VALUES
(1, 1, '2022-10-18 05:21:37'),
(2, 2, '2022-10-18 05:21:37'),
(5, 0, '2022-10-18 05:22:02'),
(6, 2, '2022-10-18 05:22:02'),
(7, 2, '2022-10-18 05:22:18'),
(8, 1, '2022-10-18 05:22:18');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `tbl_data`
--
ALTER TABLE `tbl_data`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `tbl_data`
--
ALTER TABLE `tbl_data`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
COMMIT;
What I am trying to achieve is select all rows which contains pattern of won like 0,2,2
so it will select id called 5,6,7 in above example.
if my pattern is like 1,2
it should select ids called 1 and 2 like this.
Database Fiddle is here
https://www.db-fiddle.com/#&togetherjs=RDJuie555L
I am finding solution from last hour but not able to achieve the goal. Let me know if any expert here can help me for same.
Thanks!
WITH cte AS (
SELECT id id0,
LEAD(id) OVER w id1,
LEAD(id,2) OVER w id2,
CONCAT_WS(',',
won,
LEAD(won) OVER w,
LEAD(won,2) OVER w) won_list
FROM tbl_data
WINDOW w AS (ORDER BY id)
)
SELECT tbl_data.*
FROM tbl_data
JOIN cte ON tbl_data.id IN (cte.id0, cte.id1, cte.id2)
WHERE cte.won_list = '0,2,2'
id
won
time
5
0
2022-10-18 05:22:02
6
2
2022-10-18 05:22:02
7
2
2022-10-18 05:22:18
fiddle
PS. If there exists more than one copy of needed pattern then the rows will mix. Add ORDER BY id clause to the outer query for to sort output rows and any of cte.idN (N=0..2) to its output list for to see separate pattern groups.

How to dynamically add INNER JOIN in a MySql Query?

I am working on a personal project and I don't know how to create a dynamic query with MySQL. I would like to get all version of a file stored in a database. For this purpose I replaced file by simplest name "first" in a test database created with this script:
/*
DROP DATABASE IF EXISTS request_test;
CREATE DATABASE IF NOT EXISTS request_test
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE request_test;
*/
DROP TABLE IF EXISTS first_next;
CREATE TABLE first_next(
id INT(3) UNSIGNED ,
first VARCHAR(40) NOT NULL,
ik_next INT(3) NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE first_next ADD PRIMARY KEY (`id`);
ALTER TABLE first_next MODIFY `id` INT(3) NOT NULL AUTO_INCREMENT;
ALTER TABLE first_next ADD CONSTRAINT fn1 FOREIGN KEY (ik_next) REFERENCES first_next(id);
Sample data:
INSERT INTO first_next (id, first, ik_next)
VALUES
(1, 'toto1', NULL),
(2, 'titi1', NULL),
(3, 'riri1', NULL),
(4, 'titi2', 2),
(5, 'fifi1', NULL),
(6, 'toto2', 1),
(7, 'titi3', 4),
(8, 'fifi2', 5),
(9, 'fifi3', 8),
(10, 'toto3', 6),
(11, 'loulou1', NULL),
(12, 'toto4', 10);
As you can see toto1 is replaced by successively 3 other name toto2, toto3 and toto4. With the following query I get the full history of toto1:
SELECT first.id,first.first AS initiale,
second.id,second.first AS suivante_1,
third.id,third.first AS suivante_2,
fourth.id,fourth.first AS suivante_3
FROM first_next AS first
INNER JOIN first_next AS second
ON second.ik_next = first.id
INNER JOIN first_next AS third
ON third.ik_next = second.id
INNER JOIN first_next AS fourth
ON fourth.ik_next = third.id
WHERE fourth.id = 12
ORDER BY first.id ASC;
In this case I added manually 3 "INNER JOIN" and I would like to dynamically adapt query with history of name. Imagine if toto5, toto6 exists. This is where I am stuck and I don't know how to solve my problem.

check if an array of users match exist as entries in a linking table with a matching column

I have a Channels table with the following structure:
CREATE TABLE IF NOT EXISTS `channels` (
`ID` int(20) NOT NULL,
`Name` varchar(50) NOT NULL,
`creatorID` int(11) NOT NULL,
`dateCreated` int(20) NOT NULL,
`isRead` tinyint(4) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
INSERT INTO `channels` (`ID`, `Name`, `creatorID`, `dateCreated`, `isRead`) VALUES
(1, 'chat', 260, 1456307705, 1),
(2, 'chat2', 36, 1456326568, 0),
(3, 'chat3', 260, 1456737864, 1);
I then have a userChannels table that lists all the members who have been invited into each channel. This is of the following format:
CREATE TABLE IF NOT EXISTS `userchannels` (
`ID` int(11) NOT NULL,
`userID` int(11) NOT NULL,
`channelID` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
INSERT INTO `userchannels` (`ID`, `userID`, `channelID`) VALUES
(1, 36, 1),
(2, 260, 1),
(3, 36, 2),
(4, 1657, 2),
(5, 1657, 3),
(6, 260, 3);
I am creating a web service to check if the the users passed up in a webservice to be members in this channel already exist in a previous channel. So if they already exist, I will pass back the existing ChannelID and if not, I will create a new channel and add the entries.
So if I were to pass up the user ids of 36 and 260, I should not create a new channel and instead, I should pass back the channelID of 1 as per test data provided. But if I were to pass up 36, 260 and 1657, I would need to create a new entry in the channels' table as there is no current instance of all 3 users existing in one channel.
So I am wondering what is the best way to check if the users passed up already exist in a channel?
I think I may have to make use of group by in the userChannels table and build A Dynamic query? But I'm really not sure how to implement this.
So far all I have is :
SELECT * FROM `userchannels`
WHERE userID = ? or userid = ? group by channelID
This is returning unique channels. I guess I will need to build this query dynamically to keep adding userid = ? depending on the length of the array of users passed up in the web service. But I can't see where to go from here. Any help appreciated.
SELECT * FROM `userchannels`
WHERE userID in (user ids comma ceparated) group by channelID
HAVING count(userId)=<amount of user ids>
You can use IN to list all user IDs. If for a channel COUNT= amount of user ids it means all the users are added to the same channel.

complex SQL query across several tables

I have 4 different tables:
Contest
Object
Contest_Obj
Winning
CONTEST has many objects (through contest_obj) AND winnings. A contest contains start and end dates.
OBJECT has many contests (through contest_obj)
CONTEST_OBJ contains object_id, contest_id, votes (most votes after end date of contest is a winner)
WINNING has contest_id, amount, and amount_type (dollars and euros)
I would like to be able to access this data in as few SQL calls as possible. More specifically, I am not currently storing the winner of a contest, so I have been calculating this on the fly.
I was hoping to get some help writing the SQL for:
total_winning_dollars (return total dollars won in past contests)
total_winning_euros (return total dollars won in past contests)
all_won_contests (return all contests that object is the winner along with winnings for that contest as both dollars and euros)
Code I have to determine winner of a contest:
SELECT "objects".*
FROM "objects"
INNER JOIN "contest_objs" ON "objects"."id" = "contest_objs"."object_id"
WHERE "contest_objs"."contest_id" = ?
ORDER BY votes DESC
LIMIT 1
sample table data:
CREATE TABLE `contests` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`start_date` DATETIME,
`end_date` DATETIME,
`user_id` INT,
`title` STRING
) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE `objects` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` STRING
) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE `winnings` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`contest_id` INT NOT NULL,
`comment` TEXT,
`amount_type` STRING NOT NULL,
`amount` INT NOT NULL,
`user_id` DATETIME
) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE `contest_objs` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`contest_id` INT NOT NULL,
`object_id` INT NOT NULL,
`votes` INT
) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO `objects` VALUES
(1, "Foo"),
(2, "Bar")
INSERT INTO `contests` VALUES
(1, "2012-09-16 23:30:16.220991", "2012-09-17 23:30:16.220991", 1, "Contest X"),
(2, "2012-09-17 23:30:16.220991", "2016-09-17 23:30:16.220991", 2, "Contest Y")
INSERT INTO `winnings` VALUES
(1, 1, "Giving $5", 5, "dollars", 1),
(2, 1, "Giving 2 euros", 2, "euros", 1),
(3, 1, "Giving 4 euros", 4, "euros", 2),
(4, 2, "Giving 2 euros to different contest", 2, "euros", 1)
INSERT INTO `contest_objs` VALUES
(1, 1, 1, 10),
(2, 1, 2, 12),
(3, 2, 2, 0),
(4, 2, 2, 0)
In the above example:
Contest X (over) has 2 objects in it (foo and bar). Bar is the winner of Contest X with 12 votes. Bar has won $5 dollars and 6 euros total from this contest.

Mysql - Help me alter this search query to get desired results

Following is a dump of the tables and data needed to answer understand the system:-
The system consists of tutors and classes.
The data in the table All_Tag_Relations stores tag relations for each tutor registered and each class created by a tutor. The tag relations are used for searching classes.
CREATE TABLE IF NOT EXISTS `Tags` (
`id_tag` int(10) unsigned NOT NULL auto_increment,
`tag` varchar(255) default NULL,
PRIMARY KEY (`id_tag`),
UNIQUE KEY `tag` (`tag`),
KEY `id_tag` (`id_tag`),
KEY `tag_2` (`tag`),
KEY `tag_3` (`tag`),
KEY `tag_4` (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `Tags` (`id_tag`, `tag`) VALUES
(1, 'Sandeepan'),
(2, 'Nath'),
(3, 'first'),
(4, 'class'),
(5, 'new'),
(6, 'Bob'),
(7, 'Cratchit');
CREATE TABLE IF NOT EXISTS `All_Tag_Relations` (
`id_tag` int(10) unsigned NOT NULL default '0',
`id_tutor` int(10) default NULL,
`id_wc` int(10) unsigned default NULL,
KEY `All_Tag_Relations_FKIndex1` (`id_tag`),
KEY `id_wc` (`id_wc`),
KEY `id_tag` (`id_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `All_Tag_Relations` (`id_tag`, `id_tutor`, `id_wc`) VALUES
(1, 1, NULL),
(2, 1, NULL),
(3, 1, 1),
(4, 1, 1),
(6, 2, NULL),
(7, 2, NULL),
(5, 2, 2),
(4, 2, 2),
(8, 1, 3),
(9, 1, 3);
Following is my query:-
This query searches for "first class" (tag for first = 3 and for class = 4, in Tags table) and returns all those classes such that both the terms first and class are present in the class name.
SELECT wtagrels.id_wc,SUM(DISTINCT( wtagrels.id_tag =3)) AS
key_1_total_matches,
SUM(DISTINCT( wtagrels.id_tag =4)) AS
key_2_total_matches
FROM all_tag_relations AS wtagrels
WHERE ( wtagrels.id_tag =3
OR wtagrels.id_tag =4 )
GROUP BY wtagrels.id_wc
HAVING key_1_total_matches = 1
AND key_2_total_matches = 1
LIMIT 0, 20
And it returns the class with id_wc = 1.
But, I want the search to show all those classes such that all the search terms are present in the class name or its tutor name
So that searching "Sandeepan class" (wtagrels.id_tag = 1,4) or "Sandeepan Nath" also returns the class with id_wc=1. And Searching. Searching "Bob First" should not return any classes.
Please modify the above query or suggest a new query, if possible using MyIsam - fulltext search, but somehow help me get the result.
I think this query would help you:
SET #tag1 = 1, #tag2 = 4; -- Setting some user variables to see where the ids go. (you can put the values in the query)
SELECT wtagrels.id_wc,
SUM(DISTINCT( wtagrels.id_tag =#tag1 OR wtagrels.id_tutor =#tag1)) AS key_1_total_matches,
SUM(DISTINCT( wtagrels.id_tag =#tag2 OR wtagrels.id_tutor =#tag2)) AS key_2_total_matches
FROM all_tag_relations AS wtagrels
WHERE ( wtagrels.id_tag =#tag1 OR wtagrels.id_tag =#tag2 )
GROUP BY wtagrels.id_wc
HAVING key_1_total_matches = 1 AND key_2_total_matches = 1
LIMIT 0, 20
It returns id_wc = 1.
For (6, 3) the query returns nothing.