How to join MySQL tables on date range? - mysql

I have a source table (TableA) that contains multiple records for each day. I need to left join (on the date field) it to TableB that contains a few records per year.
The problem is that TableA should be joined to the earliest record from TableB where the date from TableA <= the date from TableB.
CREATE TABLE IF NOT EXISTS `tableA` (
`id` int(6) unsigned NOT NULL,
`date` date NOT NULL,
`content` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `tableA` (`id`, `date`, `content`) VALUES
('1', '2017-10-03', 'The earth is round.'),
('2', '2018-01-01', 'The earth is flat'),
('3', '2018-01-01', 'One hundred angels can dance on the head of a pin'),
('4', '2018-01-02', 'The earth is flat and rests on a bull\'s horn'),
('5', '2018-01-03', 'The earth is like a ball.');
CREATE TABLE IF NOT EXISTS `tableB` (
`date` date NOT NULL,
`content` varchar(200) NOT NULL,
PRIMARY KEY (`date`)
) DEFAULT CHARSET=utf8;
INSERT INTO `tableB` (`date`, `content`) VALUES
('2017-01-01', 'ONE'),
('2017-12-01', 'TWO'),
('2018-01-02', 'THREE'),
('2018-01-05', 'FOUR');
Based on the this SQLFiddle, I'm looking for the following result.
tableA.id | tableB.content
--------------------------
1 | TWO
2 | THREE
3 | THREE
4 | THREE
5 | FOUR

Here is one solution:
SELECT a.id, b.content
FROM TableA a
JOIN TableB b ON b.date = (
SELECT MIN(b2.date)
FROM TableB b2
WHERE b2.date >= a.date
);
I'm not sure whether this is the most efficient way, but it works.

Related

How to update values in 3 tables irrespective of whether the value exists or not? With or without join

The problem:
When I update Tom in the redo table, I want the changes to affect the daily and lab irrespective of whether Tom exists in daily or lab. Even if he doesn't exist in daily or
lab I want the update to be made only in the redo table.
Look at the tables below:
First table redo
Second Table daily
Third Table lab
What I tried:
UPDATE redo,daily,lab SET
redo.name = '$newName', daily.name = '$newName', lab.name = '$newName',
redo.place = '$newPlace', daily.place = '$newPlace', lab.place = '$newPlace',
redo.code = '$newCode', daily.code = '$newCode', lab.code = '$newCode',
redo.age = '$newAge', daily.age= '$newAge', lab.age = '$newAge',
redo.date = redo.date, daily.date = daily.date, lab.date = lab.date,
redo.contact = '$newContact',daily.contact = '$newContact', lab.contact='$newContact',
redo.secondarycontact = '$newSecondaryContact',
daily.secondarycontact = '$newSecondaryContact',
lab.secondarycontact = '$newSecondaryContact'
WHERE redo.no='$no' AND
(redo.name=daily.name AND redo.name=lab.name) AND
(redo.place=daily.place AND redo.place=lab.place)
Result:
Values are updated only if they exist in all the 3 tables at the same time.
Given your use case and relationships, this is how I would set up the tables if it were my project:
NOTE:
I've made some assumptions about your field types in the below that may not be correct, but it should be trivial to adapt them as needed for your use case.
Using this structure will require slightly more complex or well thought out queries, but the result will be MUCH more maintainable and far less error prone.
I would normaly name columns and such differently, but I've elected to use names similar to the naming convention you seem to be using to keep it simple since this question isnt about "best practices naming columns" etc..
Create the tables
Create an 'activities' table to hold all types of activities in one place, note the "type" column with possible values of 'lab','redo','daily'
CREATE TABLE `activities` (
`no` bigint(20) NOT NULL AUTO_INCREMENT,
`consultation` varchar(255) DEFAULT NULL,
`lab` varchar(255) DEFAULT NULL,
`token` VARCHAR(45) NULL,
`detailsid` bigint(20) NOT NULL,
`foreignkey` bigint(20) DEFAULT NULL,
`type` enum('lab','redo','daily') NOT NULL,
`date` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`no`)
);
Create an 'activitiyusers' table to hold the details for the people that are related to our "activities"
CREATE TABLE `activitiyusers` (
`no` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`gender` ENUM('M', 'F') NOT NULL,
`age` SMALLINT NULL,
`contact` VARCHAR(255) NOT NULL,
`secondarycontact` VARCHAR(255) NULL,
`place` VARCHAR(255) NULL,
`code` VARCHAR(45) NULL,
`createdat` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
`updatedat` VARCHAR(45) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`no`));
Insert some test data
Insert 3 new "activitiyusers"
INSERT INTO `activitiyusers` (`name`, `gender`, `age`, `contact`, `secondarycontact`, `place`, `code`) VALUES ('Tom', 'M', '31', '1212121', '3434343', 'California', '1');
INSERT INTO `activitiyusers` (`name`, `gender`, `age`, `contact`, `secondarycontact`, `place`, `code`) VALUES ('Jack', 'M', '45', '99999', '11111', 'Colorado', '2');
INSERT INTO `activitiyusers` (`name`, `gender`, `age`, `contact`, `secondarycontact`, `place`, `code`) VALUES ('Harry', 'M', '99', '112233', '998877', 'Texas', '3');
Insert 3 new "redo" activities, one related to each of our 3 "activitiyusers"
INSERT INTO `activities` (`token`, `detailsid`, `foreignkey`, `type`) VALUES ('0', '1', NULL, 'redo');
INSERT INTO `activities` (`token`, `detailsid`, `foreignkey`, `type`) VALUES ('0', '2', NULL, 'redo');
INSERT INTO `activities` (`token`, `detailsid`, `foreignkey`, `type`) VALUES ('0', '3', NULL, 'redo');
Insert 2 new "daily" activities, one related to Tom and 'redo' activity 1, the second related to Harry and 'redo' activity 3"
INSERT INTO `activities` (`consultation`, `detailsid`, `foreignkey`, `type`) VALUES ('Cough and Cold', '1', '1', 'daily');
INSERT INTO `activities` (`consultation`, `detailsid`, `foreignkey`, `type`) VALUES ('Panadol', '3', '3', 'daily');
Insert 2 new "lab" activities, one related to Jack and 'redo' activity 2, the second related to Harry and 'redo' activity 3"
INSERT INTO `activities` (`lab`, `detailsid`, `foreignkey`, `type`) VALUES ('Blood Test', '2', '2', 'lab');
INSERT INTO `activities` (`lab`, `detailsid`, `foreignkey`, `type`) VALUES ('Injection', '3', '3', 'lab');
Examples of how to work with this data:
Note: these queries will make use of join cluases as well as Column and table aliases
Get All "activities" for "Tom", along with Tom's details
SELECT
a.no AS activityno,
a.consultation,
a.lab,
a.token,
a.type,
a.date,
au.no AS userno,
au.name,
au.gender,
au.age,
au.contact,
au.secondarycontact,
au.place,
au.code,
au.createdat,
au.updatedate
FROM
activities a
JOIN
activitiyusers au ON a.detailsid = au.no
WHERE
name = 'Tom';
Get all "redo type activities" for "Jack", along with Jack's details
SELECT
a.no AS activityno,
a.consultation,
a.lab,
a.token,
a.type,
a.date,
au.no AS userno,
au.name,
au.gender,
au.age,
au.contact,
au.secondarycontact,
au.place,
au.code,
au.createdat,
au.updatedate
FROM
activities a
JOIN
activitiyusers au ON a.detailsid = au.no
WHERE
name = 'Jack' AND a.type = 'redo';
# Given a known activity that has an id/no of '2',
# update the details for the activityuser related to that activity
UPDATE activitiyusers
SET
contact = '22222222',
age = 46,
code = 5
WHERE
no = (SELECT
detailsid
FROM
activities
WHERE
no = 2);
Given a known "repo" activity that has an id/no of '3', get all sub activities related to that activity along with details of the related activitiyuser
Note that this utilizes a mysql self join, ie we are joining the activities on itself to get subActivity rows that are related to a given redoActivity row.
SELECT
redoActivity.no AS redoactivityno,
subActivity.no AS subactivityno,
redoActivity.consultation AS redoactivityconsultation,
subActivity.consultation AS subactivityconsultation,
subActivity.lab,
redoActivity.token,
subActivity.type,
redoActivity.date AS redoactivitydate,
subActivity.date AS subactivitydate,
au.no AS userno,
au.name,
au.gender,
au.age,
au.contact,
au.secondarycontact,
au.place,
au.code,
au.createdat,
au.updatedate
FROM
activities subActivity
JOIN activities redoActivity ON subActivity.foreignkey = redoActivity.no
JOIN activitiyusers au ON redoActivity.detailsid = au.no
WHERE
redoActivity.no = 3;

Add records randomly between select result

I want to add records randomly in select result like this example:
1 a
2 a
- b
3 a
- b
4 a
5 a
6 a
- b
7 a
8 a
- b
In a simple query I select from a table but in this case I want result mix with b table without random a result otherwise I simply could union them and then order random().
EDIT:
-- ----------------------------
-- Table structure for t1
-- ----------------------------
DROP TABLE IF EXISTS `t1`;
CREATE TABLE `t1` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
INSERT INTO `t1` VALUES ('1', 'a');
INSERT INTO `t1` VALUES ('2', 'a');
INSERT INTO `t1` VALUES ('3', 'a');
INSERT INTO `t1` VALUES ('4', 'a');
INSERT INTO `t1` VALUES ('5', 'a');
INSERT INTO `t1` VALUES ('6', 'a');
INSERT INTO `t1` VALUES ('7', 'a');
INSERT INTO `t1` VALUES ('8', 'a');
INSERT INTO `t1` VALUES ('9', 'a');
INSERT INTO `t1` VALUES ('10', 'a');
-- ----------------------------
-- Table structure for t2
-- ----------------------------
DROP TABLE IF EXISTS `t2`;
CREATE TABLE `t2` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `t2` VALUES ('1', 'b');
INSERT INTO `t2` VALUES ('2', 'b');
INSERT INTO `t2` VALUES ('3', 'b');
So I can union two tables and then order by random and the result will be like this:
SELECT
t1.ID,
t1.name
FROM
t1
UNION ALL
SELECT
t2.ID,
t2.name
FROM
t2
ORDER BY RAND();
5 a
2 a
6 a
3 a
7 a
2 b
9 a
1 a
10 a
3 b
4 a
8 a
1 b
But I don't want to random records I just only want mix two tables records like first example.
I hope my question is now clear.
I don't think pure SQL is the best solution here, I would do it in some end framework (I don't know what you use), like PHP, Delphi, Java, etc. This way is more easier and you can control it better. But if you would like to do it in SQL, here an idea:
SET #max_t1 = (SELECT MAX(ID) FROM t1);
SET #max_t2 = (SELECT MAX(ID) FROM t2);
SET #last_order = RAND()*#max_t1/#max_t2;
SELECT
t1.ID, t1.name, t1.ID AS "Ord"
FROM
t1
UNION ALL
SELECT
t2.ID, t2.name, #last_order:=#last_order+RAND()*#max_t1/#max_t2
FROM
t2
ORDER BY Ord
This way we generate a random position for TableB, then in every row we add some random value to it, in according to the size differency between the two tables, then order the whole result on this column.

how to get records count of this month?

how to get records count of this month?
I want to count user of this month ? "this month" should be calculate by mysql itself. how to write the sql?
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `a`
-- ----------------------------
DROP TABLE IF EXISTS `a`;
CREATE TABLE `a` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(30) default NULL,
`date` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of a
-- ----------------------------
INSERT INTO `a` VALUES ('1', 'jimy', '2014-02-11 09:24:42');
INSERT INTO `a` VALUES ('7', 'khon', '2014-02-19 09:24:50');
INSERT INTO `a` VALUES ('3', 'tina', '2014-01-11 09:25:03');
INSERT INTO `a` VALUES ('4', 'kelvin', '2013-12-11 09:25:09');
INSERT INTO `a` VALUES ('5', 'ricky', '2014-02-12 09:25:14');
you can try this
Select count(*)
FROM a
WHERE MONTH(date) = MONTH(NOW())
GROUP BY MONTH(date)
MONTH(date) only returns the month number, it won't work with multiple years data.
SELECT COUNT(*) count
FROM a
WHERE EXTRACT(YEAR_MONTH FROM a.date) = EXTRACT(YEAR_MONTH FROM NOW())
Or you could use:
SELECT COUNT(*) count
FROM a
WHERE DATE_FORMAT(a.date,'%Y%m') = DATE_FORMAT(NOW(),'%Y%m')

Getting List of records with No related records MySQL

I wish to know which team in "TX" have not played a game. (In other words Im looking for a selection of records where there is no related record in the many table.)
Here is the SQL:
(Or if You prefer the sql fiddle is here:http://sqlfiddle.com/#!2/14106 )
CREATE TABLE `Team` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`Name` VARCHAR(45) NULL ,
`State` VARCHAR(45) NULL ,
PRIMARY KEY (`ID`) );
CREATE TABLE `Games` (
`ID` INT NOT NULL AUTO_INCREMENT,
`Team_ID` INT NULL ,
`Game_Day` DATE NULL ,
PRIMARY KEY (`ID`) );
INSERT INTO `Team` (`Name`, `State`) VALUES ('Rams', 'TX');
INSERT INTO `Team` (`Name`, `State`) VALUES ('Rockets', 'OK');
INSERT INTO `Team` (`Name`, `State`) VALUES ('Bombers', 'TX');
INSERT INTO `Team` (`Name`, `State`) VALUES ('Yellow Jackets', 'NV');
INSERT INTO `Team` (`Name`, `State`) VALUES ('Wildcats', 'CT');
INSERT INTO `Team` (`Name`, `State`) VALUES ('Miners', 'CO');
INSERT INTO `Team` (`Name`, `State`) VALUES ('Bolts', 'TX');
INSERT INTO `Games` (`Team_ID`, `Game_Day`) VALUES ('2', '2013-03-16');
INSERT INTO `Games` (`Team_ID`, `Game_Day`) VALUES ('2', '2013-01-01');
INSERT INTO `Games` (`Team_ID`, `Game_Day`) VALUES ('3', '2013-04-16');
INSERT INTO `Games` (`Team_ID`, `Game_Day`) VALUES ('5', '2013-02-02');
INSERT INTO `Games` (`Team_ID`, `Game_Day`) VALUES ('4', '2013-02-12');
INSERT INTO `Games` (`Team_ID`, `Game_Day`) VALUES ('6', '2013-01-09');
INSERT INTO `Games` (`Team_ID`, `Game_Day`) VALUES ('6', '2013-01-01');
INSERT INTO `Games` (`Team_ID`, `Game_Day`) VALUES ('3', '2013-05-01');
I should get the result:
ID Name
1 Rams
7 Bolts
SELECT `ID`, `Name` FROM `TEAM`
WHERE `ID` NOT IN (SELECT DISTINCT(`Team_ID`) from `Games`)
AND `State` = 'TX';
SqlFiddle here.
Use an outer join, selecting only those rows that don't match
SELECT t.*
FROM TEAM t
LEFT JOIN GAMES g ON g.team_id = t.id
WHERE t.state = 'TX'
AND g.team_id is null -- return only rows that *don't* join
This this running in SQL Fiddle
Note that using a join will out-perform a sub-query approach, especially when data sets become large.
You can also left-join to the Games table and filter for where there isn't a corresponding Games row. This is usually faster than NOT IN when the tables have a lot of rows:
SELECT Team.ID, Team.Name
FROM Team
LEFT JOIN Games ON Team.ID = Games.Team_ID
WHERE Team.State = 'TX' AND Games.ID IS NULL;
If there isn't a Games row to go with the Teams row, the Games.ID column will be null in the result, so if you filter on Games.ID IS NULL you'll get all the rows where a team has no games.
There's a SQL Fiddle here.
Hope this would help.
select t.ID,t.NAME
FROM Team t
WHERE t.state = 'TX'
AND t.id NOT IN (SELECT g.TEAM_ID FROM Games g)

Ordering Records by the contents of Related Table

I have two tables. One for Salesman and the other for Sales I wish to order the output of the query by the ALPHA sort of the Name of the salesman.
Here is the Table structure:
CREATE TABLE `Salesman` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`Name` VARCHAR(45) NULL ,
PRIMARY KEY (`ID`) );
INSERT INTO `Salesman` (`Name`) VALUES ('Bill');
INSERT INTO `Salesman` (`Name`) VALUES ('John');
INSERT INTO `Salesman` (`Name`) VALUES ('Dave');
INSERT INTO `Salesman` (`Name`) VALUES ('Mark');
CREATE TABLE `Sales` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`Item` INT VARCHAR(45) NULL ,
`Salemesman_ID` INT NULL ,
`Total` INT NUll ,
PRIMARY KEY (`ID`) );
INSERT INTO `Sales` (`Item`, `Salemesman_ID`, `Total`) VALUES ('Pen', '3', '14');
INSERT INTO `Sales` (`Item`, `Salemesman_ID`, `Total`) VALUES ('Rat', '1', '12');
INSERT INTO `Sales` (`Item`, `Salemesman_ID`, `Total`) VALUES ('Car', '2', '1230');
INSERT INTO `Sales` (`Item`, `Salemesman_ID`, `Total`) VALUES('Rabbit', '2', '11');
INSERT INTO `Sales` (`Item`, `Salemesman_ID`, `Total`) VALUES('Towel', '1', '6');
INSERT INTO `Sales` (`Item`, `Salemesman_ID`, `Total`) VALUES('VaporWare', '4','900');
Because the Names in Salesmen in Alpha order are: Bill, Dave, John, Mark
I need the output to be
Rat 1 12 (Bill = 1)
Towel 1 6 (Bill = 1)
Pen 3 14 (Dave = 3)
Car 2 1230 (John = 2)
Rabbit 2 11 (John = 2)
VaporWare 4 900 (Mark = 4)
You can do what you want by joining the tables together:
select s.*
from sales s join
SalesMan sm
on s.Salesman_Id = sm.Id
order by sm.name, sm.id;
I feel the need to add that the table name "Salesman" seems unnecessarily restrictive to only half the human race.