How to create UNIQUE KEY by part of field please? - mysql

My environment is Server version: 5.1.72-community MySQL Community Server (GPL).
An example:
CREATE TABLE `pe` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`person_id` int(11) NOT NULL,
`height` int(3) DEFAULT NULL,
`weight` double(5,1) DEFAULT NULL,
`pe_time` date NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_id` (`person_id`,`pe_time`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
And I have some rows in table pe, some of them like:
person_id, height, weight, pe_time
10051, 160, 55, '2017-10-20'
10052, 172, 60, '2018-01-09'
Now, given a new row like:
person_id, height, weight, pe_time
10052, 172, 61, '2018-01-10'
Because there is a row which pe_time's year is 2018 and the person_id = 10052, so I want to update that row, set each field as the new value.
But, given a new row like:
person_id, height, weight, pe_time
10051, 161, 57, '2018-01-10'
Because there is not a row which pe_time's year is 2018 and the person_id = 10051, so I want to insert the new row.
As this, I want the
UNIQUE KEY is `uk_id` (`person_id`,**`pe_time's year`**).
If no way to create that UNIQUE KEY, how to write the sql
INSERT INTO... ON DUPLICATE KEY UPDATE...
to do this please?

Related

Struggling to SELECT all elements from a table

MySQL is a new language to me and I struggle with selecting more data from my loans table when I do this query:
My objective is to print all elements of the Loans table that match the Bank IDs, all I get is outputs 1-10 where I have over 13 elements in my loans table.
EDIT 1: Bank Table serves as a link between all tables, I know the problem resides in my DML query however cluelessly not sure what to do.
When running my query, only matching primary key to foreign key appears. That is if Bank ID is 1 and Loans ID is 1 it shows, but when Bank ID is 1 and Loans ID is 13 it does not appear in the query.
Please save your criticism, as mentioned above, my experience is green.
My DML:
SELECT bank.bankID, bankcustomer.FirstName, bankcustomer.LastName, loans.FirstPaymentDate
FROM bank
JOIN bankcustomer ON bank.bankID = bankcustomer.customerID
JOIN loans ON loans.LoansID = bank.bankID;
Tables DDL:
CREATE TABLE bankCustomer(
CustomerID int(11) NOT NULL AUTO_INCREMENT,
FirstName varchar(20) DEFAULT NULL,
MiddleName varchar(20) DEFAULT NULL,
LastName varchar(20) DEFAULT NULL,
Address_Line1 varchar(50) DEFAULT NULL,
Address_Line2 varchar(50) DEFAULT NULL,
City varchar(20) DEFAULT NULL,
Region varchar(20) DEFAULT NULL,
PostCode varchar(20) DEFAULT NULL,
Country varchar(30) DEFAULT NULL,
DateOfBirth DATE DEFAULT NULL,
telephoneNumber int(13) DEFAULT 0,
openingAccount int CHECK (openingAccount >= 50),
PRIMARY KEY (CustomerID),
KEY CustomerID (CustomerID)
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE bank(
BankID int(11) NOT NULL AUTO_INCREMENT,
customerID int,
PRIMARY KEY (BankID),
FOREIGN KEY (CustomerID) REFERENCES bankCustomer(CustomerID) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE loans(
LoansID int(11) NOT NULL AUTO_INCREMENT,
BankID int,
PaymentRate int(100) DEFAULT 300,
NumOfMonthlyPayments int(12) DEFAULT NULL,
FirstPaymentDate DATE DEFAULT NULL,
MonthlyDueDate DATE DEFAULT NULL,
PRIMARY KEY (LoansID),
FOREIGN KEY (BankID) REFERENCES bank(BankID) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
INSERT DML's:
INSERT INTO bank (BankID, CustomerID) VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10);
INSERT INTO loans (LoansID, BankID, PaymentRate, NumOfMonthlyPayments, FirstPaymentDate, MonthlyDueDate) VALUES (1, 1, 400, 12, '2008-02-03', '2008-03-25'),
(11, 1, 150, 10, '2008-02-04', '2008-04-25'),
(12, 1, 150, 10, '2008-02-07', '2008-04-25'),
(2, 2, 100, 20, '2011-04-01', '2011-04-25'),
(3, 3, 85, 5, '2015-07-03', '2015-08-25')...
Thank you all for your dear help, I managed to resolve my issue. The problem was the order of JOINing clauses.
SELECT loans.LoansID, bankcustomer.FirstName, customerbankcard.AccountNumber, loans.FirstPaymentDate
FROM bank
JOIN loans ON loans.BankID = bank.bankID
JOIN bankcustomer ON bankcustomer.customerID = bank.customerID
JOIN customerbankcard ON customerbankcard.bankID = bank.bankID
GROUP BY loans.LoansID ASC;
The outcome was to avoid loop, repeating wrongly assigned account numbers with customers whose IDs did not match.
If you want to select all tables use *. Also, you are joining tables incorrectly.
SELECT bank.*, bankcustomer.*, loans.*
FROM bank
JOIN bankcustomer ON bank.customerID = bankcustomer.customerID --Since you want to join data on customer ID you select custemerID in both tables
JOIN loans ON loans.BankID = bank.bankID; --The same problem here when joining tables
Goodluck and happy coding!
Firstly, joins could only be used between the tables when the columns are common between them (though they might have different names in different tables). That is why the concept of foreign key is of paramount importance as it references the column to the referenced table as you have duly done in your DDL commands.
Secondly, try using semicolon (;) as it denotes the end of any command and might get you out of the looping.

Conditional query in the select

I am exposing my concern to you.
There are "demande_psy" and "consultant" tables. Each "demande_psy" record can relate to several "consultants" records, and each "consultant" record can relate to several "demande_psy" records, according to a "statut". This is the reason why, the primary key of the "demande_psy_consultant" link table (many to many relation table) is composed of: "id_demande_psy", "id_consultant", "statut". By querying the database , I need to retrieve a list of demande_psy and their priority statut, according to these 3 rules:
If among the same demand_psy, I have at least one AFFECTED status => my request line must display the "Affected" status
Otherwise, if among the same demande_psy, I have at least one status ACCEPTED => my demande_psy line must display "Accepted"
Otherwise my demande_psy line should show "Sent"
Here my tables creation code :
CREATE TABLE `demande_psy` (
`id` int(10) UNSIGNED NOT NULL,
`titre` varchar(255) NOT NULL,
`detail` text NOT NULL,
`sms` text NOT NULL,
`created_at` datetime NOT NULL,
`modified_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `demande_psy` (`id`, `titre`, `detail`, `sms`, `created_at`)
VALUES (1, 'Test demande', 'bla bla bla bla', 'qsdf\r\nqsdf\r\n', '2021-03-18 17:23:59');
ALTER TABLE `demande_psy` ADD PRIMARY KEY (`id`);
ALTER TABLE `demande_psy` MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
CREATE TABLE `consultant` (
`id` int(11) UNSIGNED NOT NULL,
`prenom` varchar(100) DEFAULT NULL,
`nom` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `consultant` (`id`, `prenom`, `nom`) VALUES
(52, 'Michel', 'Moral (Somica - Undici)'),
(55, 'Patrick', 'Amar');
ALTER TABLE `consultant` ADD PRIMARY KEY (`id`);
ALTER TABLE `consultant` MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=56;
CREATE TABLE `demande_psy_consultant` (
`id_demande_psy` int(10) UNSIGNED NOT NULL,
`id_consultant` int(10) UNSIGNED NOT NULL,
`statut` enum('SENT','ACCEPTED','AFFECTED') NOT NULL,
`created_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `demande_psy_consultant` (`id_demande_psy`, `id_consultant`,
`statut`, `created_at`) VALUES
(1, 52, 'SENT', '2021-03-18 17:39:59'),
(1, 55, 'ACCEPTED', '2021-03-18 19:05:45');
ALTER TABLE `demande_psy_consultant` ADD PRIMARY KEY (`id_demande_psy`,`id_consultant`,`statut`);
ALTER TABLE `demande_psy_consultant`
ADD CONSTRAINT `fk_consultant` FOREIGN KEY (`id_consultant`) REFERENCES `consultant` (`id`),
ADD CONSTRAINT `fk_demande_psy` FOREIGN KEY (`id_demande_psy`) REFERENCES `demande_psy` (`id`);
Here a simplified example :
demande_psy
-----------
1 toto
6 titi
consultant
----------
15 Marc
88 Jean
demande_psy_consultant
----------------------
1 15 SENT
1 62 SENT
1 88 ACCEPTED
1 88 AFFECTED
6 88 SENT
6 15 SENT
==> EXPECTED RESULT
---------------------------------------------------
1 88 AFFECTED (according to the 3 rules)
6 15 SENT
And now, here is my sql query:
SELECT
DPC.id_demande_psy,
DPC.id_consultant,
(CASE
WHEN (SELECT count(DPC.statut) FROM demande_psy_consultant DPC WHERE DPC.statut = "AFFECTED")>0 THEN "AFFECTED"
WHEN (SELECT count(DPC.statut) FROM demande_psy_consultant DPC WHERE DPC.statut = "ACCEPTED")>0 THEN "ACCEPTED"
ELSE "SENT"
END) as statutComputed
FROM demande_psy_consultant DPC
GROUP BY DPC.id_demande_psy
With it, I obtain :
MY (BAD) RESULT
---------------
1 15 AFFECTED
6 15 AFFECTED
So I struggle a bit to correct it to obtain the desired result If anyone has an idea, I'm interested
thank you !

mysql : remove row if parent id match in primary key columan

Actually, i don't know that what should be the title of the question.
I have two table which schema define as below:
**Table 1 (organization_master)**:
CREATE TABLE `organization_master` ( `organization_id` int(11) NOT NULL, `parent_organization_id` int(11) DEFAULT NULL, `organization_name` varchar(150) NOT NULL, `c_user_id` int(11) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `organization_master` (`organization_id`, `parent_organization_id`, `organization_name`, `c_user_id`) VALUES
(119, NULL, 'Patel-Apps', 220),
(183, 119, 'Patel-Apps sub 1', 220),
(184, 119, 'Patel-Apps sub 2', 220),
(250, 247, 'Patel-Apps2', 222);
ALTER TABLE `organization_master` ADD PRIMARY KEY (`organization_id`), ADD KEY `organization_name` (`organization_name`), ADD KEY `c_user_id` (`c_user_id`);
ALTER TABLE `organization_master` MODIFY `organization_id` int(11) NOT NULL AUTO_INCREMENT;
**Table 2 (organization_assigned_user)** :
CREATE TABLE `organization_assigned_user` ( `organization_assigned_user_id` int(11) NOT NULL, `organization_id` int(11) DEFAULT NULL, `user_id` int(11) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `organization_assigned_user` (`organization_assigned_user_id`, `organization_id`, `user_id`) VALUES (29, 250, 219),(30, 250, 220);
ALTER TABLE `organization_assigned_user` ADD PRIMARY KEY (`organization_assigned_user_id`), ADD KEY `user_id` (`user_id`), ADD KEY `organization_id` (`organization_id`);
ALTER TABLE `organization_assigned_user` MODIFY `organization_assigned_user_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=31;
when i run below query it give me output as below:
SELECT om.organization_id, om.parent_organization_id, om.organization_name FROM `organization_master` AS om LEFT JOIN organization_assigned_user AS oau ON om.organization_id = oau.organization_id WHERE om.c_user_id =220 OR oau.user_id =220 GROUP BY om.organization_id
organization_id parent_organization_id organization_name
119 NULL Patel-Apps
183 119 Patel-Apps sub 1
184 119 Patel-Apps sub 2
250 247 Patel-Apps2
Now, my question is that i don't want the rows which parent_organization_id exists in organization_id column. i.e 199 parent_organization_id exists in organization_id. so my out put should be like like below.
organization_id parent_organization_id organization_name
119 NULL Patel-Apps
250 247 Patel-Apps2
You Can try this
SELECT om.organization_id, om.parent_organization_id, om.organization_name
FROM `organization_master` AS om LEFT JOIN organization_assigned_user AS oau
ON om.organization_id = oau.organization_id
WHERE om.c_user_id =220 OR oau.user_id =220
AND Exists (SELECT 1 FROM `organization_master` o
WHERE o.organization_id = om.organization_id
AND o.parent_organization_id <> om.organization_id)
GROUP BY om.organization_id;

optimising updating player scores table with results of queries on game results

I have a table containing the results of games played. I have another table for each player showing wins, losses, draws. I would like to update the player results table by analysing the games table. Currently calculation is done in php, and due to the number of games causes a delay in our database for about 4 seconds, which causes delays in general. I was thinking of moving the operation to a stored procedure to make it faster. Can anyone recommend a clever way of doing the calculation and subsequent updates to the player_chan_stats. I would like to do it entirely in mysql queries as this would probably be faster (assumption) than php.
This is an extract of our game result table
CREATE TABLE IF NOT EXISTS `temp_game_result` (
`gam_key` bigint(20) NOT NULL COMMENT 'the game key',
`gam_pla_1` bigint(20) NOT NULL COMMENT 'player 1',
`gam_pla_2` bigint(20) NOT NULL COMMENT 'player2',
`gam_to_play` tinyint(4) NOT NULL COMMENT 'who started',
`gam_start` datetime NOT NULL,
`gam_stop` datetime DEFAULT NULL,
`gam_status` enum('playing','win','draw','lose','error') NOT NULL COMMENT 'result with reference to gam_pla_1',
`mg_cleaned` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0 if it has not passed thru cleanup, 1 otherwise',
`chn_key` bigint(20) NOT NULL COMMENT 'the tournament the game was for',
PRIMARY KEY (`gam_key`),
KEY `gam_status` (`gam_status`),
KEY `gam_start` (`gam_start`),
KEY `gam_stop` (`gam_stop`),
KEY `mg_cleaned` (`mg_cleaned`),
KEY `gam_pla_1` (`gam_pla_1`),
KEY `gam_pla_2` (`gam_pla_2`),
KEY `chn_key` (`chn_key`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `temp_game_result` (`gam_key`, `gam_pla_1`, `gam_pla_2`, `gam_to_play`, `gam_start`, `gam_stop`, `gam_status`, `mg_cleaned`, `chn_key`) VALUES
(1, 1, 2, 2, '2011-05-02 20:12:13', '2011-05-02 20:42:46', 'lose', 1, 1),
(2, 1, 2, 1, '2011-05-02 20:43:00', '2011-05-02 21:55:19', 'error', 1, 1),
(3, 2, 1, 1, '2011-05-03 21:13:18', '2011-05-03 21:14:21', 'win', 1, 1);
this is an extract of our player result table
CREATE TABLE IF NOT EXISTS `player_chan_stats` (
`pcs_key` bigint(20) NOT NULL AUTO_INCREMENT,
`pla_key` bigint(20) NOT NULL,
`chn_key` bigint(20) NOT NULL,
`pcs_seed` int(11) NOT NULL,
`pcs_rank` int(11) NOT NULL,
`pcs_games` int(11) NOT NULL DEFAULT '0',
`pcs_wins` int(11) NOT NULL DEFAULT '0',
`pcs_losses` int(11) NOT NULL DEFAULT '0',
`pcs_draws` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`pcs_key`),
UNIQUE KEY `pla_key_2` (`pla_key`,`chn_key`),
KEY `pla_key` (`pla_key`),
KEY `pcs_seed` (`pcs_seed`),
KEY `pcs_rank` (`pcs_rank`),
KEY `chn_key` (`chn_key`),
KEY `pcs_wins` (`pcs_wins`),
KEY `pcs_losses` (`pcs_losses`),
KEY `pcs_draws` (`pcs_draws`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Stats of player per channel' AUTO_INCREMENT=26354 ;
INSERT INTO `player_chan_stats` (`pcs_key`, `pla_key`, `chn_key`, `pcs_seed`, `pcs_rank`, `pcs_games`, `pcs_wins`, `pcs_losses`, `pcs_draws`) VALUES
(1, 1, 1, 1552, 1844, 325, 146, 176, 3),
(2, 2, 1, 1543, 2272, 93, 48, 43, 2);
Triggers may be your solution http://dev.mysql.com/doc/refman/5.0/en/triggers.html
a helpful trigger to you will be on insert (or update) in temp_game_result if gam_status is win update +1 to wins of the player...
will be (more or less)
CREATE TRIGGER update_wins AFTER UPDATE ON account
FOR EACH ROW
BEGIN
IF NEW.gam_status = 'win' THEN
update player_chan_stats set pcs_wins=pcs_wins+1 where psc_key=NEW.gam_pla_1;
update player_chan_stats set pcs_losses=pcs_losses +1 where psc_key=NEW.gam_pla_2;
ELSEIF NEW.gam_status = 'lose'
[...]
END IF;
END;

MySQL Select Records from 2 not-related Tables Ordering by Timestamp

I'd like to collect data from 2 different mysql tables ordering the result by a timestamp but without merging the columns of the 2 tables in a single row.
T_ONE(one_id,one_someinfo,one_ts)
T_TWO(two_id,two_otherinfo,two_ts)
Notice that the field two_otherinfo is not the same as one_someinfo, the only columns in common are id and timestamp.
The result should be a mix of the two tables ordered by the timestamp but each row, depending on the timestamp, should contain only the respective columns of the table.
For example, if the newest record comes from T_TWO that row should have the T_ONE one_someinfo column empty.
I just need to order the latest news from T_ONE and the latest messages posted on T_TWO so the tables are not related. I'd like to avoid using 2 queries and then merging and ordering the results by timestamp with PHP. Does anyone know a solution to this? Thanks in advance
This is the structure of the table
CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL auto_increment,
`fromid` int(10) NOT NULL,
`toteam` int(10) NOT NULL,
`banned` tinyint(1) NOT NULL default '0',
`replyid` int(15) default NULL,
`cont` mediumtext NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE `stars` (
`id` int(10) unsigned NOT NULL auto_increment,
`daynum` int(10) NOT NULL,
`userid` int(10) NOT NULL,
`vote` tinyint(2) NOT NULL default '3',
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
INSERT INTO `posts` (`fromid`, `toteam`, `banned`, `replyid`, `cont`, `timestamp`) VALUES(5, 12, 0, 0, 'mess posted#1', 1222222220);
INSERT INTO `posts` (`fromid`, `toteam`, `banned`, `replyid`, `cont`, `timestamp`) VALUES(5, 12, 0, 0, 'mess posted#2', 1222222221);
INSERT INTO `posts` (`fromid`, `toteam`, `banned`, `replyid`, `cont`, `timestamp`) VALUES(5, 12, 0, 0, 'mess posted#3', 1222222223);
INSERT INTO `stars` (`daynum`, `userid`, `vote`, `timestamp`) VALUES(3, 160, 4, 1222222222);
INSERT INTO `stars` (`daynum`, `userid`, `vote`, `timestamp`) VALUES(4, 180, 3, 1222222224);
The result ordering by timestamp DESC should be the second record of table stars with timestamp 1222222224 then the third record of table posts with timestamp 1222222223 and following... Since the tables have got different fields from each other, the first row of the result should contain the columns of the table stars while the columns of table posts should be empty.
The columns of a UNION must be the same name and datatype on every row. In fact, declare column aliases in the first UNION subquery, because it ignores any attempt to rename the column in subsequent subqueries.
If you need the columns from the two subqueries to be different, put in NULL as placeholders. Here's an example, fetching the common columns id and timestamp, and then fetching one custom column from each of the subqueries.
(SELECT p.id, p.timestamp AS ts, p.fromid, NULL AS daynum FROM posts)
UNION
(SELECT s.id, s.timestamp, NULL, s.daynum, FROM stars)
ORDER BY ts DESC
Also put the subqueries in parentheses, so the last ORDER BY applies to the whole result of the UNION, not just to the last subquery.
SELECT one_id AS id, one_someinfo AS someinfo, one_ts AS ts
UNION
SELECT two_id AS id, two_someinfo AS someinfo, two_ts AS ts
ORDER BY ts
SELECT one_id AS id
, one_someinfo AS one_someinfo
, NULL AS two_someinfo
, one_ts AS ts
FROM t_ONE
UNION ALL
SELECT two_id
, NULL
, two_someinfo
, two_ts
FROM t_TWO
ORDER BY ts