How to merge rows without null fields - mysql

I'm still learning MySQL and I have this table...
staff_id
Name
Monday
Tuesday
1
Mark
8:00am-5:00pm
9:00am-6:00pm
2
Steve
9:00am-6:00pm
7:00am-4:00pm
I have managed to split and insert new rows into the same table using scripts like below.
INSERT INTO table (staff_id,Name,Monday)
(SELECT staff_id, Name, "8:00am-1:00pm"
FROM table
WHERE Monday= "8:00am-5:00pm");
INSERT INTO table (staff_id,Name,Monday)
(SELECT staff_id, Name, "2:00pm-5:00pm"
FROM table
WHERE Monday= "8:00am-5:00pm");
...etc...
It works but I end up with too many rows(I'm working with thousands of rows).
Is there a way I can get a table like this below using MySQL scripts only?
staff_id
Name
Monday
Tuesday
1
Mark
8:00am-1:00pm
9:00am-2:00pm
1
Mark
2:00pm-5:00pm
3:00pm-6:00pm
2
Steve
9:00am-2:00pm
7:00am-12:00pm
2
Steve
3:00pm-6:00pm
1:00pm-4:00pm
other solutions suggest using aggregate functions(MAX) which unfortunately won't work in my case and I can't figure out how to properly use "JOINs" for this purpose.
Any help would be really appreciated.

You have to use UPDATE and not INSERT INTO
CREATE TABLE mytable
(`staff_id` int, `Name` varchar(5), `Monday` varchar(13), `Tuesday` varchar(13))
;
INSERT INTO mytable
(`staff_id`, `Name`, `Monday`, `Tuesday`)
VALUES
(1, 'Mark', '8:00am-5:00pm', '9:00am-6:00pm'),
(2, 'Steve', '9:00am-6:00pm', '7:00am-4:00pm')
;
UPDATe mytable SET Monday = "8:00am-1:00pm"
WHERE Monday= "8:00am-5:00pm";
UPDATe mytable SET Monday = "8:00am-1:00pm"
WHERE Monday= "9:00am-6:00pm";
SELECT * FROM mytable
staff_id | Name | Monday | Tuesday
-------: | :---- | :------------ | :------------
1 | Mark | 8:00am-1:00pm | 9:00am-6:00pm
2 | Steve | 8:00am-1:00pm | 7:00am-4:00pm
db<>fiddle here

Related

How can I store the direction of a bus route in mysql database?

I have 3 table: Route, Bus_stop, Route-Bus_stop
Route: id, weekday, weekend
Bus_stop: id, name
Route-Bus_stop: primary key(order, time), foreign key: idRoute, idBus_stop
Insert into Route(id,weekday,weekend) values(1,1,1);
Insert into Route(id,weekday,weekend) values(2,1,1);
Insert into Bus_stop(id,name) values(1,'A');
Insert into Bus_stop(id,name) values(2,'B');
Insert into Bus_stop(id,name) values(3,'C');
Insert into Route-Bus_stop(idRoute,idBus_stop,routeo_rder,stoptime) values(1,1,0,7:00);
Insert into Route-Bus_stop(idRoute,idBus_stop,route_order,stoptime) values(1,2,1,7:10);
Insert into Route-Bus_stop(idRoute,idBus_stop,route_order,stoptime) values(1,3,2,7:30);
Insert into Route-Bus_stop(idRoute,idBus_stop,route_order,stoptime) values(2,3,0,15:00);
Insert into Route-Bus_stop(idRoute,idBus_stop,route_order,stoptime) values(2,2,1,15:10);
Insert into Route-Bus_stop(idRoute,idBus_stop,route_order,stoptime) values(2,1,2,15:20);
-- this is only an example, my database is bigger.
One of the route goes A -> B -> C, the other C->B->A
If the user wants to go A to C, how can I show only one of the routes with all of the bus stations to the destination?
I've write this query, but this shows both of the routes.
select *idRoute from Route-Bus_stop where idBus_stop=1 or idBus_stop=3 order by time; // A -> C
This will show route A->C and C-> A. But I want only the A->C
First don_'t rßuse reserved words like ORDER or TIME as column names, so you spre the need to use backticks all the tiome
Also Route-Bus_stop is also not valid without Backticks.
For your query you keed a selfjoin to get starting point and end point
CREATE TABLE Route_Bus_stop (
idRoute int
,idBus_stop int
,`order` int
,`time` varchar(10))
Insert into Route_Bus_stop(idRoute,idBus_stop,`order`,`time`) values(1,1,0,'7:00');
Insert into Route_Bus_stop(idRoute,idBus_stop,`order`,`time`) values(1,2,1,'7:10');
Insert into Route_Bus_stop(idRoute,idBus_stop,`order`,`time`) values(1,3,2,'7:30');
Insert into Route_Bus_stop(idRoute,idBus_stop,`order`,`time`) values(2,3,0,'15:00');
Insert into Route_Bus_stop(idRoute,idBus_stop,`order`,`time`) values(2,2,1,'15:10');
Insert into Route_Bus_stop(idRoute,idBus_stop,`order`,`time`) values(2,1,2,'15:20');
SELECT r1.idRoute,r1.idBus_stop,r2.idBus_stop,r1.`time`, r2.`time`
FROM Route_Bus_stop r1 INNER JOIN Route_Bus_stop r2
ON r1.idRoute = r2.idRoute AND r1.`order` < r2.`order`
WHERE r1.idBus_stop = 1
AND r2.idBus_stop = 3
idRoute | idBus_stop | idBus_stop | time | time
------: | ---------: | ---------: | :--- | :---
1 | 1 | 3 | 7:00 | 7:30
db<>fiddle here

From a MySQL table select one or more rows and update columns from each one [duplicate]

This question already has answers here:
MySQL - UPDATE query with LIMIT
(9 answers)
Closed 2 years ago.
I have a table with pre-existing giveaway codes and I need to select one or more rows and then update three columns of each row with personal identification, customer code, and "reserved" status. This to reserve each row until receiving a response from our client's API.
The table look like this:
code identification customer_code status
-----------------------------------------------------------------
81Ow3tCs1nNwxKu -- -- available
I1NdH9F22S7RhU3 -- -- available
Xc942LWe8Z6nt8x -- -- available
zcLMRO8kSeM7S06 -- -- available
K94erORvzSsU0ik -- -- available
Tried with this but got an error:
UPDATE promo_codes
SET
identification='12345',
customer_code='67890',
status='reserved'
FROM
(SELECT code FROM promo_codes WHERE status='available' LIMIT 2);
Then I tried with REPLACE INTO but also with error:
REPLACE INTO promo_codes(identification,customer_code,status)
VALUES('12345','67890','reserved')
WHERE
(SELECT code FROM promo_codes WHERE status='available' LIMIT 2);
I do not know what else to do. Could someone give me an idea?
Thank you very much for the help.
A little rewriting and you code works
You should consider adding a ORDER BY RAND() , because a LIMIT without an order is quite meaningless
CREATE TABLE promo_codes (
`code` VARCHAR(15),
`identification` VARCHAR(20),
`customer_code` VARCHAR(20),
`status` VARCHAR(9)
);
INSERT INTO promo_codes
(`code`, `identification`, `customer_code`, `status`)
VALUES
('81Ow3tCs1nNwxKu', '--', '--', 'available'),
('I1NdH9F22S7RhU3', '--', '--', 'available'),
('Xc942LWe8Z6nt8x', '--', '--', 'available'),
('zcLMRO8kSeM7S06', '--', '--', 'available'),
('K94erORvzSsU0ik', '--', '--', 'available');
UPDATE promo_codes
SET
identification='12345',
customer_code='67890',
status='reserved'
WHERE status='available' LIMIT 2;
SELECT * FROM promo_codes
code | identification | customer_code | status
:-------------- | :------------- | :------------ | :--------
81Ow3tCs1nNwxKu | 12345 | 67890 | reserved
I1NdH9F22S7RhU3 | 12345 | 67890 | reserved
Xc942LWe8Z6nt8x | -- | -- | available
zcLMRO8kSeM7S06 | -- | -- | available
K94erORvzSsU0ik | -- | -- | available
db<>fiddle here

Creating Primary key from 2 autonumber and constant letter when creating table

I am new to MYSQL and would like to create a table where a constant Letter depicting the department is added to an auto increment number. This way I would be able to identify the category of the worker upon viewing the ID.
Ex. Dept A and employee 135. The ID I am imaging should read A135 or something similar. I have created the table, the auto increment works fine, the constant letter has been declared and is featuring. However I would like to concatenate them in order to use the A135 as a primary key.
Any Help Please?
This quite tricky, and you would be probably better off doing manual concatenation in a select query.
But since you asked for it...
In normal usage you would have used a computed column for this, but they do not support using autoincremented columns in their declaration. So you would need to use triggers:
on insert, query information_schema.tables to retrieve the autoincremented id that is about to be assigned and use it to generate the custom id
on update, reset the custom id
Consider the following table structure:
create table workers (
id int auto_increment primary key,
name varchar(50) not null,
dept varchar(1) not null,
custom_id varchar(12)
);
Here is the trigger for insert:
delimiter //
create trigger trg_workers_insert before insert ON workers
for each row
begin
if new.custom_id is null then
select auto_increment into #nextid
from information_schema.tables
where table_name = 'workers' and table_schema = database();
set new.custom_id = CONCAT(new.dept, lpad(#nextid, 11, 0));
end if;
end
//
delimiter ;
And the trigger for update:
delimiter //
create trigger trg_workers_update before update ON workers
for each row
begin
if new.dept is not null then
set new.custom_id = CONCAT(new.dept, lpad(old.id, 11, 0));
end if;
end
//
delimiter ;
Let's run a couple of inserts for testing:
insert into workers (dept, name) values ('A', 'John');
insert into workers (dept, name) values ('B', 'Jim');
select * from workers;
| id | name | dept | custom_id |
| --- | ---- | ---- | ------------ |
| 1 | John | A | A00000000001 |
| 2 | Jim | B | B00000000002 |
And let's test the update trigger
update workers set dept = 'C' where name = 'Jim';
select * from workers;
| id | name | dept | custom_id |
| --- | ---- | ---- | ------------ |
| 1 | John | A | A00000000001 |
| 2 | Jim | C | C00000000002 |
Demo on DB Fiddle
Sorry, my answer does not fit in a comment.
I agree with #GMB.
This is a tricky situation and in some cases (selects mainly) will lead in a performance risk due you'll have to split PK in where statements, which is not recommended.
Having a column for department and another for auto_increment is more logical. And the only gap you have is to know the number of employees per department you'll have to make a count grouping by dept. Instead of a max() splitting your concatenated PK, which is is at high performance cost.
Let atomic and logic data remain in separate columns. I would suggest to create a third column with the concatenated value.
If, for some company reason, you need B1 and A1 values for employees of different departments, I'd suggest to have 3 columns
Col1 - letter(not null)
Col2 - ID(Not auto-increment, but calculated as #GMB's solution) (Not NULL)
Col3 - Concatenation of Col1 and Col2 (not null)
PK( Col1, col2)

Uderstanding DECODE and ENCODE in MYSQL

I needed to save id in such a way that would make it encrypted and so that it would take up exactly 8 characters.
I did that using the following command:
SELECT encode(LPAD(id,4,0),'abc')
This command turns an id = 1
into 0001 and then turns that into a code fe5ab21a
How do I decrypt this code?
Below is an example of a select and the result it generates
SELECT 0001
, DECODE(ENCODE('0001', 'abc'), 'abc')
, UNHEX(DECODE(ENCODE('0001', 'abc'), 'abc'))
, ENCODE('0001','abc')
, DECODE('fe5ab21a', 'abc')
, UNHEX(DECODE('fe5ab21a', 'abc'))
, HEX('0001')
The result:
1
30303031
0001
fe5ab21a
68d357a7005dcbe0
NULL
30303031
As I understand, you want to make like a short unique key-pass that's linked to one of your ID.
Be careful with this kind of encryption, if someone want to break it, it'll take less than a few minutes, it's really weak. But if it's only for some obfuscation without security risk, it's ok.
To improve a bit the security (but decrease the user experience), try to use AES_ENCRYPT()
The wrong thing with your code is that you don't force the type of input/output, here is something with the proper typing.
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE t
(`id` int, `name` varchar(7))
;
INSERT INTO t
(`id`, `name`)
VALUES
(1, 'hello'),
(2, 'hola'),
(3, 'bonjour')
;
Query 1:
select *, ENCODE(id,'KEY') as encrypted_id from t
Results:
| id | name | encrypted_id |
|----|---------|--------------|
| 1 | hello | Vw== |
| 2 | hola | yw== |
| 3 | bonjour | iA== |
Query 2:
SELECT * from t where id = CAST(DECODE(FROM_BASE64('yw=='),'KEY') AS CHAR(50))
Results:
| id | name |
|----|------|
| 2 | hola |

Reorder rows in a MySQL table

I have a table:
+--------+-------------------+-----------+
| ID | Name | Order |
+--------+-------------------+-----------+
| 1 | John | 1 |
| 2 | Mike | 3 |
| 3 | Daniel | 4 |
| 4 | Lisa | 2 |
| 5 | Joe | 5 |
+--------+-------------------+-----------+
The order can be changed by admin hence the order column. On the admin side I have a form with a select box Insert After: to entries to the database. What query should I use to order+1 after the inserted column.
I want to do this in a such way that keeps server load to a minimum because this table has 1200 rows at present. Is this the correct way to save an order of the table or is there a better way?
Any help appreciated
EDIT:
Here's what I want to do, thanks to itsmatt:
want to reorder row number 1 to be after row 1100, you plan to leave 2-1100 the same and then modify 1 to be 1101 and increment 1101-1200
You need to do this in two steps:
UPDATE MyTable
SET `Order` = `Order` + 1
WHERE `Order` > (SELECT `Order`
FROM MyTable
WHERE ID = <insert-after-id>);
...which will shift the order number of every row further down the list than the person you're inserting after.
Then:
INSERT INTO MyTable (Name, `Order`)
VALUES (Name, (SELECT `Order` + 1 FROM MyTable WHERE ID = <insert-after-id>));
To insert the new row (assuming ID is auto increment), with an order number of one more than the person you're inserting after.
Just add the new row in any normal way and let a later SELECT use ORDER BY to sort. 1200 rows is infinitesimally small by MySQL standards. You really don't have to (and don't want to) keep the physical table sorted. Instead, use keys and indexes to access the table in a way that will give you what you want.
you can
insert into tablename (name, `order`)
values( 'name', select `order`+1 from tablename where name='name')
you can also you id=id_val in your inner select.
Hopefully this is what you're after, the question isn't altogether clear.