MYSQL: select values when found in join, placeholder otherwise - mysql

I have a MYSQL database with three tables: MEETS, SWIMMERS, SWIMS, which I use to store a swim team's results.
What I am trying to do is write a select query which, for a given swimmer and event, generates a column which has one entry for each meet in the database - if the swimmer actually swam the event at the meet, select the time, if not, select a placeholder. Basically, I'm trying to de-raggedize my data:
I'm able to join the tables successfully, but what's below only returns the Meets where the swimmer HAS swum the event, not all meets. (I realize this is kind of a silly thing to need, but the graphing control I'm trying to feed it into is really finicky about what it will accept)
edit: Attempting to do better with providing a miniminal example.
Here are the CREATE and INSERT queries for the three tables:
CREATE TABLE MEETS ( M_ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT );
CREATE TABLE SWIMMERS ( SwimmerId INTEGER PRIMARY KEY AUTOINCREMENT, FirstName TEXT, LastName TEXT );
CREATE TABLE SWIMS ( SwimID INTEGER PRIMARY KEY AUTOINCREMENT, SwimmerID INTEGER, MeetID INTEGER, Event TEXT, TimeSec REAL );
INSERT into MEETS values (null,'Meet1');
INSERT into MEETS values (null,'Meet2');
INSERT into MEETS values (null,'Meet3');
INSERT into SWIMMERS values(null,'Fred','Barnes');
INSERT into SWIMS values(null,1,1,'50 Free',30.95);
INSERT into SWIMS values(null,1,2,'100 Free',66.25);
INSERT into SWIMS values(null,1,4,'50 Free',29.33);
Here's my test query for a particular swimmer and event:
SELECT B.M_ID , A.TimeSec
FROM SWIMS AS A LEFT JOIN MEETS AS B ON B.M_ID = A.MeetId
WHERE A.SwimmerID = 1 and A.Event = '50 Free'
this results in:
M_ID
TimeSec
1
30.95
3
29.33
Desired result:
M_ID
TimeSec
1
30.95
2
0 (or NULL)
3
29.33
I think the problem is that the where clause is false on the missing row, but I'm not sure how to solve it. Maybe a union, but I can't figure out how to structure it. Any help greatly appreciated!

If you need all meets you need to fetch from meets then join the others.
SELECT A.M_ID , B.TimeSec
FROM MEETS AS A
LEFT JOIN SWIMS AS B ON A.M_ID = B.MeetId AND B.SwimmerID = 1 and B.Event = '50 Free'

Related

copy data from source table every 1hr from the point where it left

I am trying to come with mysql query/procedure which basically copies data( not all data and not from all fields) to another table which already exists but the fields name in this table is different that the source field. Since this sql query will be run every 1 hr through some scheduler, it needs to have a logic which should know/check the source table so that it doesn't copy the same data again. A flag would be required in source table then I think.
Table : Product Revenue date A MC 2020-10-11 B VI 2020-10-12 C MC 2020-10-12 D MC 2020-10-13
This table keeps adding records live from a front end application. So the query to copy this data after ever 1 hr has to start coping from where it left last time.
create table DemoTable (
ClientId int NOT NULL AUTO_INCREMENT PRIMARY KEY,
ClientName varchar(20),
isMarried tinyint(1) DEFAULT 1
);
INSERT INTO DemoTable(ClientName,isMarried) values('Larry',0);
INSERT INTO DemoTable(ClientName) values('David');
INSERT INTO DemoTable(ClientName,isMarried) values('Mike',1);
INSERT INTO DemoTable(ClientName) values('Carol');
If your another table also store primary key(ClientID) from table DemoTable then you can do following without any modification in database structure.
Table Structure:
DemoTable - ClientID(Pk), CilentName, isMarried
DemoTable_New - Id(PK), ClientID, ClientName
INSERT INTO DemoTable_New (ClientId, ClientName)
SELECT d.ClientId, d.ClientName
FROM DemoTable d
LEFT JOIN DemoTable_New dn ON d.ClientId = dn.ClientId -- Make sure to do Left Join
WHERE dn.Id IS NULL; -- Only pick for which record was not found in DemoTable_New

Query in MySQL to Find Distinct Status Based on Primary Key

My question is regarding a MySQL query that I am trying to write. I have written some psuedo-code to help illustrate what query I am trying to write:
SELECT *
FROM persons AS p
INNER JOIN person_info AS pi
ON p.person_id = pi.person_id
WHERE status MAY INCLUDE lost, missing, or found
WHAT person_id has no instances of the found status
I'd like to know for each person_id (which can have multiple statuses), which do not have any instance of the status "found." I'm not concerned with just the records of lost and missing. I want to find the unique cases where there is no "found" status based on each unique, distinct person_id.
If I'm understand correctly, one option is to use not in:
select *
from persons
where personid not in (
select personid
from person_info
where status = 'found'
)
This will return all records from the persons table that don't have a matching record in the person_info table with status = 'found'.
Alternatively you can use left join/null check. Not exists can work, but may be slower with mysql. There are some potential issues with null checks as well. Depends on desired results at that point.
This is as far as I took it #sgeddes. In writing it I realized it just makes peoples eyes glaze over.
SQL NOT IN () danger
create table mStatus
( id int auto_increment primary key,
status varchar(10) not null
);
insert mStatus (status) values ('single'),('married'),('divorced'),('widow');
create table people
( id int auto_increment primary key,
fullName varchar(100) not null,
status varchar(10) null
);
Chunk1:
truncate table people;
insert people (fullName,status) values ('John Henry','single');
select * from mstatus where status not in (select status from people);
** 3 rows, as expected **
Chunk2:
truncate table people;
insert people (fullName,status) values ('John Henry','single'),('Kim Billings',null);
select * from mstatus where status not in (select status from people);
no rows, huh?
Obviously this is 'incorrect'. It arises from SQL's use of three-valued logic,
driven by the existence of NULL, a non-value indicating missing (or UNKNOWN) information.
With NOT IN, Chunk2 it is translated like this:
status NOT IN ('married', 'divorced', 'widowed', NULL)
This is equivalent to:
NOT(status='single' OR status='married' OR status='widowed' OR status=NULL)
The expression "status=NULL" evaluates to UNKNOWN and, according to the rules of three-valued logic,
NOT UNKNOWN also evaluates to UNKNOWN. As a result, all rows are filtered out and the query returns an empty set.
Possible solutions include:
select s.status
from mstatus s
left join people p
on p.status=s.status
where p.status is null
or use not exists

Insert into master table when detail records present but missing master

I have two tables - one master, one detail (i.e. a one-to-many pair of tables). I'm importing data from a horrible schema and one feature of the data is that often I have some detail records but no master.
How would go about inserting master records in these cases? I can locate the missing masters easily enough with this query:
select * from p_ltx_surgical_comp as c -- detail
left join p_ltx_surgical as s -- master
on c.fk_oid = s.fk_oid -- this is the key
where s.oid is null -- primary key, so null means no record exists
group by c.fk_oid; -- only show one value even if there are multiple detail records
Oh, and as an extra wrinkle, I only want to insert a single master even if there a are multiple detail records.
You can start with this INSERT query:
INSERT INTO p_ltx_surgical (fk_oid)
SELECT DISTINCT c.fk_oid
FROM
p_ltx_surgical_comp AS c
LEFT JOIN p_ltx_surgical AS s
ON c.fk_oid = s.fk_oid
WHERE
s.oid IS NULL
and you can add more details to your table, for example:
INSERT INTO p_ltx_surgical (fk_oid, description, ...)
SELECT DISTINCT c.fk_oid, 'missing record', ...
FROM
...
Ah, I was so close... this seems to have worked:
insert into p_ltx_surgical (oid, fk_oid, ltx_surg_date)
select sp_getvdtablekey('p_ltx_surgical', 0), c.fk_oid, '1900-01-01' from
p_ltx_surgical_comp as c -- detail
left join p_ltx_surgical as s -- master
on c.fk_oid = s.fk_oid -- this is the key
where s.oid is null
group by c.fk_oid; -- primary key, so null means no record exists

Update with Subquery never completes

I'm currently working on a project with a MySQL Db of more than 8 million rows. I have been provided with a part of it to test some queries on it. It has around 20 columns out of which 5 are of use to me. Namely: First_Name, Last_Name, Address_Line1, Address_Line2, Address_Line3, RefundID
I have to create a unique but random RefundID for each row, that is not the problem. The problem is to create same RefundID for those rows whose First_Name, Last_Name, Address_Line1, Address_Line2, Address_Line3 as same.
This is my first real work related to MySQL with such large row count. So far I have created these queries:
-- Creating Teporary Table --
CREATE temporary table tempT (SELECT tt.First_Name, count(tt.Address_Line1) as
a1, count(tt.Address_Line2) as a2, count(tt.Address_Line3) as a3, tt.RefundID
FROM `tempTable` tt GROUP BY First_Name HAVING a1 >= 2 AND a2 >= 2 AND a3 >= 2);
-- Updating Rows with First_Name from tempT --
UPDATE `tempTable` SET RefundID = FLOOR(RAND()*POW(10,11))
WHERE First_Name IN (SELECT First_Name FROM tempT WHERE First_Name is not NULL);
This update query keeps on running but never ends, tempT has more than 30K rows. This query will then be run on the main DB with more than 800K rows.
Can someone help me out with this?
Regards
The solutions that seem obvious to me....
Don't use a random value - use a hash:
UPDATE yourtable
SET refundid = MD5('some static salt', First_Name
, Last_Name, Address_Line1, Address_Line2, Address_Line3)
The problem is that if you are using an integer value for the refundId then there's a good chance of getting a collision (hint CONV(SUBSTR(MD5(...),1,16),16,10) to get a SIGNED BIGINT). But you didn't say what the type of the field was, nor how strict the 'unique' requirement was. It does carry out the update in a single pass though.
An alternate approach which creates a densely packed seguence of numbers is to create a temporary table with the unique values from the original table and a random value. Order by the random value and set a monotonically increasing refundId - then use this as a look up table or update the original table:
SELECT DISTINCT First_Name
, Last_Name, Address_Line1, Address_Line2, Address_Line3
INTO temptable
FROM yourtable;
set #counter=-1;
UPDATE temptable t SET t,refundId=(#counter:=#counter + 1)
ORDER BY r.randomvalue;
There are other solutions too - but the more efficient ones rely on having multiple copies of the data and/or using a procedural language.
Try using the following:
UPDATE `tempTable` x SET RefundID = FLOOR(RAND()*POW(10,11))
WHERE exists (SELECT 1 FROM tempT y WHERE First_Name is not NULL and x.First_Name=y.First_Name);
In MySQL, it is often more efficient to use join with update than to filter through the where clause using a subquery. The following might perform better:
UPDATE `tempTable` join
(SELECT distinct First_Name
FROM tempT
WHERE First_Name is not NULL
) fn
on temptable.First_Name = fn.First_Name
SET RefundID = FLOOR(RAND()*POW(10,11));

Combine two queries to check for duplicates in MySQL?

I have a table that looks like this:
Number | Name
--------+--------
123 | Robert
This is what I want to do:
If the Number is already in the database, don't insert a new record.
If the Number is not in the databse, but the name is, create a new name and insert it. So for example, if I have a record that contains 123 for Number and Bob for Name, I don't want to insert it, but if I get a record that contains 456 for Number and Robert for name, I would insert 456 and Robert1. I was going to check for duplicates individually like:
SELECT * FROM Person where Number = 123;
//If number is not found
SELECT * FROM Person where Name = 'Robert';
//If name is found, add a number to it.
Is there a way I can combine the two statements?
There are actually two problems in your question. The first problem is to make Number column unique and the second one is to increment the column Name by appending a number if it already exists.
FIRST PART
Since the number is UNIQUE, enforce a UNIQUE constraint on the column. It could be a PRIMARY KEY or a UNIQUE KEY.
If the column has no KEY and you want to make it PRIMARY, here is the ALTER statement:
ALTER TABLE TableName ADD CONSTRAINT tb_pk PRIMARY KEY (Number)
SQLFiddle Demo
but if you only want it to be UNIQUE and not a primary key,
ALTER TABLE TableName ADD CONSTRAINT tb_uq UNIQUE (Number)
SQLFiddle Demo
SECOND PART
You can actually do it without using join.
INSERT INTO TableName(Number, Name)
SELECT 124 AS Number,
CONCAT('Robert', COALESCE(MAX(CAST(REPLACE(Name, 'Robert', '0') AS UNSIGNED)) + 1,'')) AS Name
FROM TableName
WHERE Name LIKE 'Robert%'
SQLFiddle Demo
SQLFiddle Demo (added more example)
SQLFiddle Demo (throws exception due to uniqueness)
Some details:
when the value supplied on column Number already exists, it will throw an error since the column is unique. I have read a comment from a deleted posts saying: "..Number is not unique, but if it does exist, I don't want to enter a record." -- it does not make any sense if you don't want to add uniqueness on the column. How will you know if the number already exists or not? Doing a little check for the existence of Number feels like a little overhead for me. So my best recommendation is to enforce uniqueness.
SELECT * FROM Person WHERE Number = 123 OR Name = 'Robert'
I haven't worked with SQL for some time, so this may be wrong ;)
Edit:
$number = 123;
$name = 'Robert';
$query = mysql_query("SELECT * FROM Person WHERE Number = $number OR Name = '$name' ");
if (mysql_num_rows($query) == 0 ) {
//-> Add your record, it's unused
} else if (mysql_result($query, 0, 'number') == $number && mysql_result($query, 0, 'name' == $name)) {
//combination of number and name already exists -> modify name and add record
} else {
echo "Number is used by another name";
}
Use this query, for insert the row [123, 'Robert']. if you want insert other values, change 123 & Robert values in below query:
insert into Person (Number,Name)
select 123, IF(mn.MaxNumber is NULL,'Robert',concat('Robert',mn.MaxNumber+1))
from (SELECT 'foo') foo
left JOIN (select max(CONVERT(SUBSTR(Name,LENGTH('Robert')+1),UNSIGNED)) `MaxNumber`
from person where name rlike '^Robert[0-9]*$') mn on 1=1
where Not Exists (select * from Person where Number=123)
NOTE: if Robert exists in the table, above query inserts Robert1. if Robert1 exists, it inserts Robert2, and so on .
make both number and name unique.
ALTER TABLE `person` ADD UNIQUE (`number` ,`name`);
You can now do a insert with ON DUPLICATE
INSERT INTO `person` (`number`, `name`, `id`) VALUES ('322', 'robert', 'NULL') ON DUPLICATE KEY UPDATE `id`='NULL';
For appending a number after name i would suggest using autoincrement column instead.
insert into Person (Number,Name)
select 123, IF(mn.MaxNumber is NULL,'Robert',concat('Robert',mn.MaxNumber+1))
from (SELECT 'foo') foo
left JOIN (select max(CONVERT(SUBSTR(Name,LENGTH('Robert')+1),UNSIGNED)) `MaxNumber`
from person where name rlike '^Robert[0-9]*$') mn on true
where Not Exists (select * from Person where Number=123)