mySQL table with EMP_ID and lots of timestamps - mysql

I'm new to relation databases and mySQL, I am trying to develop a database for employees, that logs all the times its employees access the system(shown by recording the timestamp of everytime it access the system).
So when the employee accesses the system, the current timeStamp is recorded, and the next time they acess it that current timestamp is also recorded. The idea is that i can go back and query how many times in a day an employee accessed the system or week and so on, for any employee.
so far i have:
EMP_ID | F_Name | L_Name | TimeStamp
-------------------------------------
1222 | joe | blogs | 12.03.22
1222 | joe | blogs | 12.44.34
1352 | carl | mansy | 19.33.22
and so on, i would like to know if there is a way to have just one emp_id show up with all the timestamps below, or do i need another table? or can i just have the data base like this?
Obviously this will grow in size a lot, so would it be better to have a table for every emp_id?
Thanks in advance Jonny

You should have 2 tables
first one is the employee table
emplyee : EMP_ID | F_Name | L_Name
the second one is the log table
employee_log : EMP_ID | TimeStamp
the first table will store the data of the empolyee
the second will store just the log of this employee
and if you want to retrieve the logs you just need to join betwen these tables
select * from employee
left join employee_log on employee.EMP_ID = employee_log.EMP_ID

You should have 2 tables one to store employee data and the other to store
the log data
employee : EMP_ID | F_Name | L_Name
employee_log : EMP_ID | TimeStamp

Assuming all data is stored in table named my_log_table, To see all timestamps for an EMP_ID 12222, query it like
select TimeStamp from my_log_table where EMP_ID = 12222
Having all logs in same table would be scalable and easier to use.
Issues with having multiple log tables:
For each new user, you have to manually create a new table
Privileges have to be granted to this script/user to query the new
table created for a new user
If EMP_ID changes, then you have to track and change table names
Moreover, you are not saving any space other than one column of EMP_ID of
combined log table

I hope it would be better if you maintain your table as below.
EMP_ID | TimeStamp
1222 | 12.03.22
1222 | 12.44.34
1352 | 19.33.22
So, while retrieving you can display as cross table like below
1222 | 1352 |
12.03.22 | 19.33.22
12.44.34 |

Related

How to get a starting and ending reservation date for a group in mysql database?

I have a database called global and in that database I have groups table and reservations table, and the relation is one to many (one group can have many reservations).
What I wanted to do was to display the group name along with the first check in date and the last check out date of the reservations.
I am developing using codeigniter 3.x.
Assuming you have this structure of tables:
GROUPS
+--------+-----------+
| id | Name |
+--------+-----------+
and:
RESERVATIONS
+--------+-----------+------------------------------+------------------------------+
| id | Groupid | ReservationCheckinDate | ReservationCheckoutDate |
+--------+-----------+------------------------------+------------------------------+
the SQL to get the desired result would be:
SELECT MIN(ReservationCheckinDate), MAX(ReservationCheckoutDate), Name FROM RESERVATIONS
JOIN GROUPS ON GROUPS.id = RESERVATIONS.Groupid
GROUP BY Name
ORDER BY Name;
hope it helps

How to generate a unique id based on different id category?

I have a table as shown below
| id | name | doc_no |
|:-----------|------------:|:------------:|
| 1 | abc | D11710001
| 2 | efg | D21710001
| 3 | hij | D31710001
| 4 | klm | D41710001
| 5 | nop | D51710001
| 1 | qrs | D11710002
I want to generate an unique id based on the id given. For example, when i have item to be stored in this table, it will generate an unique id based on the id of the table.
Note: The id in this table is a foreign key. The doc no can be modified by user into their own format manually.
The id format - D 'id' 'year' 'month' 0001(auto increment)
How can i write the sql to generate unique id during storing data?
Continuing with the comment by #strawberry I might recommend not storing the ID in your database. Besides the fact that accessing the auto increment ID at the same time you are inserting the record might be tricky, storing this generated ID would be duplicating the information already stored elsewhere in your table. Instead of storing your ID, just generate it when you query, e.g.
SELECT
id, name, doc_no,
CONCAT('D', id, STR_TO_DATE(date, '%Y-%m'), auto_id) AS unique_id
FROM yourTable;
This assumes that you would be storing the insertion date of each record in a date column called date. It also assumes that your table has an auto increment column called auto_id. Note that having the date of insertion stored may be useful to you in other ways, e.g. if you want to search for data in your table based on date or time.
You could create Trigger and update the column or you can write the update state just after your INSERT
insert into <YOUR_TABLE>(NAME,DOC_NO) values('hello','dummy');
update <YOUR_TABLE> set DOC_NO=CONCAT('D',
CAST(YEAR(NOW()) AS CHAR(4)),
CAST(MONTH(NOW()) AS CHAR(4)),
LAST_INSERT_ID())
WHERE id=LAST_INSERT_ID();
Please note, as above SQL may cause race condition, when simultaneously server get multiple requests.
#Tim Biegeleisen has good point though, as it is better to construct the id when you are SELECTing the data.

How to design my database to accommodate this data

I am developing a database for a payroll application, and one of the features I'll need is a table that stores the list of employees that work at each store, each day of the week.
Each employee has an ID, so my table looks like this:
| Mon | Tue | Wed | Thu | Fri | Sat | Sun
Store 1 | 3,4,5 | 3,4,5 | 3,4,5 | 4,5,7 | 4,5,7 | 4,5,6,7 | 4,5,6,7
Store 2 | 1,8,9 | 1,8,9 | 1,8,9 | 1,8,9 | 1,8,9 | 1,8,9 | 1,8,9
Store 3 | 10,12 | 10,12 | 10,12 | 10,12 | 10,12 | 10,12 | 10,12
Store 4 | 15 | 15 | 15 | 16 | 16 | 16 | 16
Store 5 | 6,11,13 | 6,11,13 | 6,11,13 | 14,18,19| 14,18,19| 14,18,19| 14,18,19
My question is, how do I represent that on my database? I came up with the following ideas:
Idea 1: Pretty much replicate the design above, creating a table with the following columns: [Store_id | Mon | Tue ... | Sat | Sun] and then store the list of employee IDs of each day as a string, with IDs separated by commas. I know that comma-separated lists are not good database design, but sometimes they do look tempting, as in this case.
Store_id | Mon | Tue | Wed | Thu | Fri | Sat
---------+---------+---------+---------+---------+---------+---------
1 | '3,4,5' | '3,4,5' | '3,4,5' | '4,5,7' | '4,5,7' | '4,5,6,7'
2 | '1,8,9' | '1,8,9' | '1,8,9 '| '1,8,9' | '1,8,9' | '1,8,9'
Idea 2: Create a table with the following columns: [Store_id | Day | Employee_id]. That way each employee working at a specific store at a specific day would be an entry in this table. The problem I see is that this table would grow quite fast, and it would be harder to visualize the data at the database level.
Store_id | Day | Employee_id
---------+-----+-------------
1 | mon | 3
1 | mon | 4
1 | mon | 5
1 | tue | 3
1 | tue | 4
Any of these ideas sound viable? Any better way of storing the data?
if I were you I would store the employee data and stores data in separate tables... but still keep the design of your main table. so do something like this
CREATE TABLE stores (
id INT, -- make it the primary key auto increment.. etc
store_name VARCHAR(255)
-- any other data for your store here.
);
CREATE TABLE schedule (
id INT, -- make it the primary key auto increment.. etc
store_id INT, -- FK to the stores table id
day VARCHAR(20),
emp_id INT -- FK to the employees table id
);
CREATE TABLE employees
id INT, -- make it the primary key auto increment.. etc
employee_name VARCHAR(255)
-- whatever other employee data you need to store.
);
I would have a table for stores and for employees as that way you can have specific data for each store or employee
BONUS:
if you wanted a query to show the store name with the employees name and their schedule and everything then all you have to do is join the two tables
SELECT s.store_name, sh.day, e.employee_name
FROM schedule sh
JOIN stores s ON s.id = sh.store_id
JOIN employees e ON e.id = sh.emp_id
this query has limitations though because you cannot order by days so you could get data by random days.. so in reality you also need a days table with specific data for the day that way you can order the data by the beginning or end of the week.
if you did want to make a days table it would just be the same thing again
CREATE TABLE days(
id INT,
day_name VARCHAR(20),
day_type VARCHAR(55)
-- any more data you want here
)
where day name would be Mon Tue... and day_type would be Weekday or Weekend
and then all you would have to do for your query is
SELECT s.store_name, sh.day, e.employee_name
FROM schedule sh
JOIN stores s ON s.id = sh.store_id
JOIN employees e ON e.id = sh.emp_id
JOIN days d ON d.id = sh.day_id
ORDER BY d.id
notice the two colums in the schedule table for day would be replaced with one column for the day_id linked to the days table.
hope thats helpful!
The second design is correct for a relational database. One employee_id per row, even if it results in multiple rows per store per day.
The number of rows is not likely to get larger than the RDBMS can handle, if your example is accurate. You have no more than 4 employees per store per day, and 5 stores, and up to 366 days per year. So no more than 7320 rows per year, and perhaps less.
I regularly see databases in MySQL that have hundreds of millions or even billions of rows in a given table. So you can continue to run those stores for many years before running into scalability problems.
I upvoted John Ruddell's answer, which is basically your option #2 with the addition of tables to hold data about the store and the employee. I won't repeat what he said, but let me just add a couple of thoughts that are too long for a comment:
Never ever ever put comma-separated values in a database record. This makes the data way harder to work with.
Sure, either #1 or #2 makes it easy to query to find which employees are working at store 1 on Friday:
Method 1:
select Friday_employees from schedule where store_id='store 1'
Method 2:
select employee_id from schedule where store_id=1 and day='fri'
But suppose you want to know what days employee #7 is working.
With method 2, it's easy:
select day from schedule where employee_id=7
But how would you do that with method 1? You'd have break the field up into it's individual pieces and check each piece. At best that's a pain, and I've seen people screw it up regularly, like writing
where Friday_employees like '%7%'
Umm, except what if there's an employee number 17 or 27? You'll get them too. You could say
where Friday_employees like '%,7,%'
But then if the 7 is the first or the last on the list, it doesn't work.
What if you want the user to be able to select a day and then give them the list of employees working on that day?
With method 2, easy:
select employee_id from schedule where day=#day
Then you use a parameterized query to fill in the value.
With method 1 ...
select employee_id from schedule where case when #day='mon' then Monday_employees when #day='tue' then Tuesday_employees when #day='wed' then Wednesday_employees when #day='thu' then Thursday_employees when #day='fri' then Friday_employees when #day='sat' then Saturday_employees as day_employees
That's a beast, and if you do it a lot, sooner or later you're going to make a mistake and leave a day out or accidentally type "when day='thu' then Friday_employees" or some such. I've seen that happen often enough.
Even if you write those long complex queries, performance will suck. If you have a field for employee_id, you can index on it, so access by employee will be fast. If you have a comma-separated list of employees, then a query of the "like '%,7,%' variety requires a sequential search of every record in the database.

Associative table with date

In my application I have association between two entities employees and work-groups.
This association usually changes over time, so in my DB I have something like:
emplyees
| EMPLOYEE_ID | NAME |
| ... | ... |
workgroups
| GROUP_ID | NAME |
| ... | ... |
emplyees_workgroups
| EMPLOYEE_ID | GROUP_ID | DATE |
| ... | ... | ... |
So suppose I have an association between employee 1 and group 1, valid from 2014-01-01 on.
When a new association is created, for example from 2014-02-01 on, the old one is no longer valid.
This structure for the associative table is a bit problematic for queries, but I actually would avoid to add an END_DATE field to the table beacuse it will be a reduntant value and also requires the execution of an insert + update or update on two rows every time a change happens in an association.
So have you any idea to create a more practical architecture to solve my problem? Is this the better approach?
You have what is called a slowly changing dimension. That means that you need to have dates in the employees_workgroup table in order to find the right workgroup at the right time for a set of employees.
The best way to handle this is to have to dates, which I often call effdate and enddate on each row. This greatly simplifies queries, where you are trying to find the workgroup at a particular point in time. Such a query might look like with this structure:
select ew.*
from employees_workgroup ew
where MYDATE between effdate and enddate;
Now consider the same results using only one date per field. It might be something like this:
select ew.*,
from employees_workgroup ew join
(select employee_id, max(date) as maxdate
from employees_workgroup ew2
where ew2.employee_id = ew.employee_id and
ew2.date <= MYDATE
) as rec
on ew.employee_id = rec.employee_id and ew.adte = ew.maxdate;
The expense of doing an update along with the insert is minimal compared to the complexity this will introduce in the queries.

MySQL pull data from same table, SUM multiple columns with conditions

I have created a users table that holds names and phone numbers (users).
id| name | phone
1 | Frank | 0345221234
2 | Sara | 0342555939
I got another table that holds a log with user's calls to different numbers (call_logs):
number | destination | price
0345221234 | destination | x /// This is Frank
0345221234 | destination | y /// This is also Frank
0342555939 | destination | z /// This is Sara
And then I have a table that holds numbers that Frank and Sara are allowed to call (allowed_numbers):
number
033485733
045727728
082358288
I would like to loop through my users table and based on their number to check the calls log table and select the SUM of price column for log records where destination does not match the allowed numbers table so that I know the cost for calls not in the allowed list.
Then I want to select SUM of price column for log records where destination DO match
the allowed numbers table so that I know how much did the allowed calls cost.
Is there any way I can do this in a single query with sub-queries and all needed in order to achieve this result set:
users number | SUM(price) of allowed calls | SUM(price) of calls not allowed
Thank you!
SELECT call_logs.number
,SUM(IF(allowed_numbers.number IS NOT NULL,call_logs.price,0)) AS AllowedPrice
,SUM(IF(allowed_numbers.number IS NULL,call_logs.price,0)) AS NotAllowedPrice
FROM call_logs
LEFT JOIN allowed_numbers
ON call_logs.destination = allowed_numbers.number
GROUP BY call_logs.number;