How to occupy something (in this case a wardrobe slot) in MySQL - mysql

I'm developing a wardrobe application that uses a database table called "entrances".
The program is used to organize a normal wardrobe storage where the storage can have different amount of numbers/slots to hang clothes on. When a customer comes up to the merchant, the merchant scans the customer's bar code and will then get a free number from the system to hang the customer's clothes on. But there can of course only be one entry for each number.
My entrances db could look something like:
ID | wardrobeNo | storeID | customerBarcode | deliveredTime | collectedTime
---+------------+---------+-----------------+---------------+--------------
1 | 1 | 1 | XX | 20:12:55 | NULL
2 | 2 | 1 | XA | 20:44:44 | NULL
3 | 1 | 2 | XZ | 20:55:55 | NULL
4 | 2 | 2 | XC | 22:22:22 | NULL
Later that day the same entries do still exist in the DB but they will now have a collected time if the clothes have been collected from the wardrobe on some of the numbers before people went home.
ID | wardrobeNo | storeID | customerBarcode | deliveredTime | collectedTime
---+------------+---------+-----------------+---------------+--------------
1 | 1 | 1 | XX | 20:12:55 | 23:23:23
2 | 2 | 1 | XA | 20:44:44 | NULL
3 | 1 | 2 | XZ | 20:55:55 | 22:23:23
4 | 2 | 2 | XC | 22:22:22 | NULL
I will then be able to see the occupied numbers with:
SELECT * FROM db WHERE storeID = x AND delivered NOT NULL AND collected = NULL
What i'm wondering about is how I would be able to lock these 'wardrobeNo' while the merchant is handling payment, so another merchant does not make order on the same 'wardrobeNo'... just like a restaurant that would link orders to tables.
Is this even a good way to tackle the problem or is there something a lot smarter? Or should I consider thinking about this problem in another way.
Hope it makes sense..

Updated: Instead of taking care of maintaining a sequence yourself, use MySQL's auto_increment in combination with a scheduled alter table command at midnight:
CREATE TABLE idTable (
idKey INT(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (idKey)
)
And at midnight:
TRUNCATE TABLE idTable;
ALTER TABLE idTable AUTO_INCREMENT = 1;
Then simply add a new record to idTable prior to adding a row to your wardrobe table and use the inserted ID (via mysql_insert_id()) to get a daily unique ID.

Related

SQL - get records from many-to-many relations by the user itself -OR- his group

I have two database tables, one as the main table and the other as the relation table.
The first table is a table of contents and the second table is a table that connects to users or groups.
Some data may also be modified in this second table.
I'm not sure about the structure and performance.
for example, we have User Id 160 which is under group id 7
So for the first, we have a post Table.
id | title | content | cover | status
------------------------------------------------
1 | first | content 1 | /img/... | 1
2 | second | content 2 | /img/... | 1
3 | another | content 3 | /img/... | 1
4 | four | content 4 | /img/... | 1
5 | five | content 5 | /img/... | 1
and for the second we have a post_rel Table:
id | group_id | user_id | post_id | title | cover | sort | status
---------------------------------------------------------------------------
1 | 7 | NULL | 1 | g title | img/... | 1 | 1
2 | NULL | 160 | 1 | u title | NULL | 2 | 1 *** selected for user_id
3 | 7 | NULL | 2 | NULL | img/... | 6 | 0
4 | NULL | 160 | 2 | NULL | img/... | 4 | 1 *** selected for user_id
5 | NULL | 160 | 3 | some | img/... | 3 | 1 *** selected for user_id
6 | 7 | NULL | 4 | NULL | img/... | 9 | 1 *** selected for group_id
7 | NULL | 165 | 5 | NULL | img/... | 5 | 0
This is the basic query we have.
select
`post_rel`.`title` as `custom_title`,
`post_rel`.`cover` as `custom_cover`,
`post_rel`.`group_id`,
`post_rel`.`user_id`,
`post`.*
from
`post`
inner join `post_rel` on `post`.`id` = `post_rel`.`post_id`
where
`post`.`status` = 1
and `post_rel`.`status` = 1
and (
`post_rel`.`user_id` = 160
or (
`post_rel`.`group_id` = 7
and `post_rel`.`post_id` not in (
select
`post_rel`.`post_id`
from
`post_rel`
where
`post_rel`.`user_id` = 160
)
)
)
order by
`post_rel`.`sort` asc
So, what you think about the basic query? Especially in the subquery, won't performance drop in a large table? Is it possible to write a better and simpler query or change the structure?
Edit: this is sqlfiddle example of my code and structure http://sqlfiddle.com/#!9/ed9d4b/1
I would change it to use "not exists" instead of "not in" and would use aliases so I could pull it off like so:
select
b.`title` as `custom_title`,
b.`cover` as `custom_cover`,
b.`group_id`,
b.`user_id`,
a.*
from
`post` a
inner join `post_rel` b on a.`id` = b.`post_id`
where
a.`status` = 1
and b.`status` = 1
and (
b.`user_id` = 160
or (
b.`group_id` = 7
and not exists (
select
'x'
from
`post_rel` c
where
c.`user_id` = 160 and c.`post_id`=b.`post_id`
)
)
)
order by
b.`sort` asc
Typically when managing users and group, there's this notion of an exception user who directly can get assigned to assets just like the whole group. This seems to be an example of that.
From a modeling-only perspective, there are 2 ways to deal with that:
Ensure that every user exists in a group and that you only assign assets to groups. For the exception user, create a group. You could even enforce that every user belongs to only one group. This way your post_rel table deals with only groups. Unfortunately, the relationship between group and user is not understood well enough to weigh in appropriately.
Driven only by the need to eliminate null values towards a good model which also reduces overhead, the other option is to use name value pairs and allows the User and Group to exist in the same field with another field besides it, denoting Group or User.
These are the SQL Fiddle:
NOT EXISTS version: http://sqlfiddle.com/#!9/1af8cf/2
NOT IN version: http://sqlfiddle.com/#!9/1af8cf/1
Some reading on nulls https://dev.mysql.com/doc/refman/5.6/en/data-size.html
Specifically:
Declare columns to be NOT NULL if possible. It makes SQL operations faster, by enabling better use of indexes and eliminating overhead for testing whether each value is NULL. You also save some storage space, one bit per column. If you really need NULL values in your tables, use them. Just avoid the default setting that allows NULL values in every column.

WHERE statement with dynamic input

I have two tables. The first one (item) is listing apartments. The second (feature) is a list of features that an apartment could have. Currently we list about 25 different features.
As every apartment can have a different set of features, I think it makes sense to have a 1:1 relationship between items and features table.
If in feature table for one the features the value is '1', this means that the linked apartment has this feature.
+-------------+------------+--------------+-------------+------------+
| table: item | | | | |
+-------------+------------+--------------+-------------+------------+
| id | created_by | titel | description | address |
+-------------+------------+--------------+-------------+------------+
| 10 | user.id | Nice Flat | text | address.id |
+-------------+------------+--------------+-------------+------------+
| 20 | user.id | Another Flat | text | address.id |
+-------------+------------+--------------+-------------+------------+
| 30 | user.id | Bungalow | text | address.id |
+-------------+------------+--------------+-------------+------------+
| 40 | user.id | Apartment | text | address.id |
+-------------+------------+--------------+-------------+------------+
+----------------+---------+--------------+----------------+--------------+------+
| table: feature | | | | | |
+----------------+---------+--------------+----------------+--------------+------+
| id | item_id | key_provided | security_alarm | water_supply | lift |
+----------------+---------+--------------+----------------+--------------+------+
| 1 | 10 | 1 | 0 | 0 | 1 |
+----------------+---------+--------------+----------------+--------------+------+
| 2 | 20 | 0 | 1 | 1 | 0 |
+----------------+---------+--------------+----------------+--------------+------+
| 3 | 30 | 1 | 1 | 0 | 1 |
+----------------+---------+--------------+----------------+--------------+------+
| 4 | 40 | 1 | 1 | 1 | 1 |
+----------------+---------+--------------+----------------+--------------+------+
I want to build a filter functionality so user can select to show only apartments with certain features.
e.g.:
$key_provided = 1;
$security_alarm = 1;
$water_supply = 0;
Does this database approach sounds reasonable for you?
What’s the best way to build a MySQL query to retrieve only apartments where the filter criteria match, keeping in mind that the number of features can be grow in future?
A better approach is to have a features table. In your case, they all seem to be binary -- yes or no -- so you can get away with:
create table item_features (
item_feature_id int auto_increment primary key,
item_id int not null,
feature varchar(255)
foreign key item_id references items(item_id)
);
The data would then have the positive features, so the first item would be:
insert into item_features (item_id, feature)
values (1, 'key_provided'), (1, 'lift');
This makes it easy to manage the features, particularly adding new ones. You might want to use a trigger, check constraint, or reference table to validate the feature names themselves, but I don't want to stray too far from your question.
Then checking for features is a little more complicated, but not that much more so. One method is explicitly using exists and not exists for each desired/undesired one:
select i.*
from items i
where exists (select 1
from item_features itf
where itf.item_id = i.item_id and
itf.feature = 'key_provided'
) and
exists (select 1
from item_features itf
where itf.item_id = i.item_id and
itf.feature = 'security_alarm'
) and
not exists (select 1
from item_features itf
where itf.item_id = i.item_id and
itf.feature = 'water supply'
);
For your existing data structure, you can filter as follows:
select i.*
from item i
inner join feature f
on f.item_id = i.id
and f.key_provided = 1
and f.security_alarm = 1
and f.water_supply = 0
This will give you all the apartments that satisfy the given criteria. For more criterias, you can just add more conditions to the on part of the join.
As a general comment about your design:
since you are creating a 1-1 relationship between apartments and features, you might as well consider having a single table to store them (spreading the information over two tables does not have any obvious advantages)
your design is OK as long as features do not change too often, since, basically, everytime a new feature is created, you need to add more columns to your table. If features are added (or removed) frequently, this can become heavy to manage; in that case, you could consider having a separated table where each (item, feature) tuple is stored in a different row, which will make this of things easier to do (with the downside that queries will get more complicated to write)

Database design for 150 million records p.a. with categories and sub categories

I need some help for a MySQL database design. The MySQL database should handle about 150 million records a year. I want to use the myisam engine.
The data structure:
Car brand (>500 brands)
Every car brand has 30+ car models
Every car model has the same 5 values, some model have additional values
Every value has exactly 3 fields:
timestamp
quality
actual value
The car brand can have some values with the same fields
The values are tracked every 5 minutes -> 105120 records a year
About the data:
The field quality should be always 'good' but when it's not I need to know.
The field timestamp is usually the but at least one value has a different timestamp
Deviation: 1-60 seconds
If the timestamp has a different timestamp it has always a different timestamp
Sometimes I don't get data because the source server is down.
How I want to use the data for
Visualisations in chart(time and actual value) with a selection of values
Aggregation of some values for every brand
My Questions:
I thought it's a good idea to split the data into different tables, so I put every brand in an extra table. To find the table by car brand name I created an index table. Is this a good practice?
Is it better to create tables for every car model (about 1500 tables)?
Should I store the quality (if it is not 'good') and the deviation of the timestamp in a seperate table?
Any other suggestions?
Example:
Table: car_brand
| car_brand | tablename | Address |
|-----------|-----------|-------------|
| BMW | bmw_table | the address |
| ... | ... | ... |
Table: bmw_table (105120*30+ car models = more than 3,2 million records per year)
| car_model | timestamp_usage | quality_usage | usage | timestamp_fuel_consumed | quality_usage |fuel_consumed | timestamp_fuel_consumed | quality_kilometer | kilometer | timestamp_revenue | quality_revenue | revenue | ... |
|-------------|---------------------|---------------|-------|-------------------------|----------------|--------------|-------------------------|-------------------|-----------|---------------------|-----------------|---------|-----|
| Z4 | 2015-12-12 12:12:12 | good | 5% | 2015-12-12 12:12:12 | good | 10.6 | 2015-12-12 12:11:54 | good | 120 | null | null | null | ... |
| Z4 | 2015-12-12 12:17:12 | good | 6% | 2015-12-12 12:17:12 | good | 12.6 | 2015-12-12 12:16:54 | good | 125 | null | null | null | ... |
| brand_value | null |null | null | null | null | null | null | null | null | 2015-12-12 12:17:12 | good | 1000 | ... |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
And the other brand tables..
Edit: Queries and quality added
Possible Queries
Note: I assume that the table bmw_table has an extra column that is called car_brand and the table name is simple_table instead of bmw_table to reduce complexity.
SELECT car_brand, sum(revenue), avg(usage)
FROM simple_table
WHERE timestamp_usage>=2015-10-01 00:00:00 AND timestamp_usage>=2015-10-31 23:59:59
GROUP BY car_brand;
SELECT timestamp_usage,usage,revenue,fuel_consumed,kilometer
FROM simple_table
WHERE timestamp_usage>=2015-10-01 00:00:00 AND timestamp_usage>=2015-10-31 23:59:59;
Quality Values
I collect the data from an OPC Server so the qualtiy field contains one of the following values:
bad
badConfigurationError
badNotConnected
badDeviceFailure
badSensorFailure
badLastKnownValue
badCommFailure
badOutOfService
badWaitingForInitialData
uncertain
uncertainLastUsableValue
uncertainSensorNotAccurate
uncertainEUExceeded
uncertainSubNormal
good
goodLocalOverride
Thanks in advance!
Droider
Do not have a separate table per brand. There is no advantage, only unnecessary complexity. Nor 1 table per model. In general, if two table look the same, the data should be combined into a single table. In your example, that one table would have brand and model as columns.
Indexes are your friend for performance. Let's see the queries you will perform, so we can discuss the optimal indexes.
What will you do if the data quality is not 'good'? Simply display "good" or "not good"?

Troubles conceptualizing a query

I have a 'Course' table and an 'Event' table.
I would like to have all the courses that actually take place, i.e. they are not cancelled by an event.
I have done this by a simple request for all the course and a script analysis (basically some loops), but this request take a time that I believe too long. I think what I want is possible in one query and no loops to optimize this request.
Here are the details :
'Course' c have the fields 'date', 'duration' and a many to many relation with the 'Grade' table
'Event' e have the fields 'begin', 'end', 'break' and a many to many relation with the 'Grade' table
A course is cancelled by an event if they occur at the same time and if the event is a break (e.break = 1)
A course is cancelled by an event if all the grades of the course are in the events that occurs at the same time (many events can occurs, I have to sum up the grades of these events and compare them to the grades of the courses). This is the part I'm doing with a loop, I have some trouble to conceptualize that.
Any help is welcome,
Thanks in advance,
PS : I'm using mysql
EDIT : Tables details
-Course
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| date | datetime | NO | | NULL | |
| duration | time | NO | | NULL | |
| type | int(11) | NO | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
+-------+---------------------+----------+------+
| id | date | duration | type |
+-------+---------------------+----------+------+
| 1 | 2013-12-10 10:00:00 | 02:00:00 | 0 |
| 2 | 2013-12-11 10:00:00 | 02:00:00 | 0 |
+-------+---------------------+----------+------+
-Event
+-------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| begin | datetime | NO | | NULL | |
| end | datetime | YES | | NULL | |
| break | tinyint(1) | NO | | NULL | |
+-------------+-------------+------+-----+---------+----------------+
+----+---------------------+---------------------+-------+
| id | begin | end | break |
+----+---------------------+---------------------+-------+
| 1 | 2013-12-10 00:00:00 | 2013-12-11 23:59:00 | 1 |
+----+---------------------+---------------------+-------+
-course_grade
+-----------+----------+
| course_id | grade_id |
+-----------+----------+
| 1 | 66 |
| 2 | 65 |
| 2 | 66 |
+-----------+----------+
-event_grade
+----------+----------+
| grade_id | event_id |
+----------+----------+
| 66 | 1 |
+----------+----------+
So here, only the course 2 should appear, because course 1 has only one grade, and this grade has an event.
I like riddles, this is a nice one, has many solutions, I think
As you say 'Any help is welcome', I give an answer altough its not the solution (and it does not fit into a comment)
I dont know, if you just want (A) the naked statement (over and out), or if you want (B) to understand how to get to the solution, I take (B)
I start with 'what would I change' before starting about the solution:
you are mixing date,datetime,start,end and duration, try to use only one logic (if it is your model ofcourse) ie.
an event/course has a start and an end time (or start/duration)
duration should (IMHO) not be a time
try to find a smallest timeslice for events/course (are there 1 sec events? or is a granularity of 5' (ie. 10:00, 10:05, 10:10 ans so on) a valid compromise?
My solution, a prgmatic one not academic
(sounds funny, but does work good in a simillar prob I had see annotation)
Create a table (T_TIME_OF_DAY) having all from 00:00, 00:05, .. 23:55
Create a Table (T_DAYS) in a valid and usefull range (a year?)
the carthesian product - call it points in time - (ie. select date, time from T_DAYS,T_TIME_OF_DAY no condition) of them (days x times) 300*24*12 ~ 100.000 rows if you need to look at a whole year (and 5' are ok for you) - thats not much and no prob
the next step is to join the curses fitting to your points in time (and the rows are down to <<100.000)
if you next join these with your events (again using point in time) you should get what you want.
simplyfied quarters of a day:
12 12 12 12 12 12 12 12
08 09 10 11 12 13 14 15
|...|...|...|...|...|...|...|...
grade 65 (C).............2..................
grade 66 (C).........1...2..................
grade 65 (E)................................
grade 66 (e)........1111..................
(annotation: I use this logic to calculate the availabillity of services regarding to their downtimes per Month / Year, and could use the already in timeslices prepared data for display)
(second answer, because it is a totaly different and mor3 standard aproach)
I made an SQLFiddle for you
so what to do:
and thats the a solution:
step one (in mind) select course,grades (lets call them C)
step two (in mind) select events, grades (lets call them E)
and - tada -
select all from C where there a no rows in E that have the same grade and the same date(somehow) and eventtype='break'
so your solution:
select
id, date start_time, date+duration end_time, grade_id
from Course c join course_grade cg on c.id=cg.course_id
where not exists (
select grade_id, begin start_time, end end_time
from event_grade eg join event e on eg.event_id=e.id
where
eg.grade_id=cg.grade_id
and e.break=1
and
(
(e.begin<=c.date and e.end >=c.date+c.duration)
or e.begin between c.date and c.date+c.duration
or e.end between c.date and c.date+c.duration
)
)
I did take no attention to optimize here

Data Entry Tracking (Database Design)

I have developed a website (PHP) that allow staffs to add records on to our system.
Staffs will be adding thousands of records into our database.
I need a way to keep track of what record have been done and the process/status of record.
Here a number of Teams I could think of:
Data Entry Team
Proof Reading Team
Admin Team
When staff (Data Entry Team) completed a record - he/she will then click on the Complete button. Then somehow it should asssign to 'Proof Reading Team' automatically.
A record need to be checked twice from a Proof Reading Team. If StaffB finish proof reading then another member from Proof Reading Team need to check it again.
When Proof reading is done, Admin Team will then assign "Record Completed"
In a few months later record might need to be updated (spelling mistake, price change, etc) - Admin might to assign record to Data entry team.
Is this good data entry management solution? How do I put this into Database Design perspective?
Here what I tried:
mysql> select * from records;
+----+------------+----------------------+
| id | name | address |
+----+------------+----------------------+
| 1 | Bill Gates | Text 1 Text Text 1 |
| 2 | Jobs Steve | Text 2 Text 2 Text 2 |
+----+------------+----------------------+
mysql> select * from staffs;
+----+-----------+-----------+---------------+
| id | username | password | group |
+----+-----------+-----------+---------------+
| 1 | admin1 | admin1 | admin |
| 2 | DEntryA | DEntryA | data_entry |
| 3 | DEntryB | DEntryB | data_entry |
| 4 | PReadingA | PReadingA | proof_reading |
| 5 | PReadingB | PReadingB | proof_reading |
+----+-----------+-----------+---------------+
mysql> select * from data_entry;
+----+------------+-----------+------------------------+
| id | records_id | staffs_id | record_status |
+----+------------+-----------+------------------------+
| 1 | 2 | 3 | data_entry_processiing |
| 2 | 2 | 3 | data_entry_completed |
| 3 | 2 | 4 | proof_read_processing |
| 4 | 2 | 4 | proof_read_completed |
| 5 | 2 | 5 | proof_read_processing |
| 6 | 2 | 5 | proof_read_completed |
+----+------------+-----------+------------------------+
Is there alternative better solution of database design?
i think design it's well done. but may be you want to separate group into groups table, and record_status into status table. If you're storing a lot of records you would store a lot of useless information, at least create an enum type for record_status field and group field
table: groups
id - name 1 - admin 2 - data_entry 3 - proof_reading
...
table: status
id - name 1 - data_entry_processing ...
and if you want the users to be in different groups at a time, you could create users_group table
table: user_groups
group_id - user_id 1 - 1 2 - 1 1 - 4 3 -
4 4 - 4 ....
Hope this helps