Manage hours and data in sql query - mysql

I'm planning to study a small webapp in php. I am faced with a problem concerning an SQL query to be performed.
Basically I have to manage reservations based on the date and time. I have separated the two things in a form with a type = date and a type = time.
The date is "data".
The hour is "orario_inizio".
this is my form:
Data: <br><input type="date" name="data" ><br>
Orario Inizio: <br><input type="time" name="orario_inizio" min="09:30:00" max="16:30:00"><br>
Orario Fine: <br><input type="time" name="orario_fine" min="10:00:00" max="18:30:00"><br>
In order for a reservation to be entered in the database it is necessary that "orario_inizio" we have a space of two hours, that is that in the following two hours there are no other bookings.
input.php
// Richiedente
$nominativo = $_POST['nominativo'];
$email = $_POST['email'];
$oggetto = $_POST['oggetto'];
$data = $_POST['data'];
$orario_inizio = $_POST['orario_inizio'];
$orario_fine = $_POST['orario_fine'];
//inserting data order
$query1 = "INSERT INTO Prenotazione (nominativo,email,data,orario_inizio,orario_fine,oggetto,nominativoi,emaili,nominativoe,emaile) VALUES ('$nominativo','$email','$data','$orario_inizio','$orario_fine','$oggetto','$nominativoi','$emaili','$nominativoe','$emaile')"; $result1 = mysqli_query($conn, $query1 );
$result1 = mysqli_query($conn, $query1 );
?>
What I can not understand is how to develop a query that allows me to consider for the date chosen by the user that there are no bookings within two hours. Let me explain with an example:
The user1 wants to make a reservation per day 24 / sept at 10:30 am. I have to make sure that there are no reservations until 12:30. If there are no other bookings already made then I would like to enter this reservation if I send an alert.
I have done the second part, the insert into the database, but i don't have idea how to do about the first check.
$query1 = "INSERT INTO Prenotazione (nominativo,email,data,orario_inizio,orario_fine,oggetto,nominativoi,emaili,nominativoe,emaile) VALUES ('$nominativo','$email','$data','$orario_inizio','$orario_fine','$oggetto','$nominativoi','$emaili','$nominativoe','$emaile')";
I have read that a possibility should be the use of SUBDATE
Can you help me? thank you
UPDATE.
Table Prenotazione
database

Since you provided no table schema, I'll have to use placeholder column names etc.
As explained through comments earlier, you can use the mysql function BETWEEN to see if something is "between" the given times (works for dates also). What it does, is it returns a boolean value. 1 if true, 0 if false.
assuming your table has a date row in order to see the bookings for specific dates, you could write the following example:
$sql="SELECT
COUNT(column_id)
FROM
table_name
WHERE
TIME($orario_inizio) BETWEEN '10:30' AND '12:30'
AND date_column=CURDATE()";
$result_set=mysqli_query($conn, $sql);
$checkIfValid=mysqli_fetch_array($conn, $result_set)[0];
if($checkIfValid > 0) {
//throw error
}
else {
//perform insert query
}
You can tweak the logic to fit your liking a bit better. For instance, CURDATE() just takes the current date, but perhaps you have a specific booking date you can use?
This SQL basically counts an id of your choice (user id or whatnot) based on the condition that the time is between 10:30 AM and 12:30 AM, and that the date is today (could be a given booking date instead).
Now, if this sql finds any records, it will be put into the count. So it either has a value of 0 or higher. If the count is higher than 0, then that means we are busy or however the logic is applied, meaning that you cannot make a booking for that day / time period, and so we throw an error. However, if the count is not bigger than 0, then we proceed with our INSERT, because then everything must be okay.

Consider the following:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table(booking_id SERIAL PRIMARY KEY,booking_start INT NOT NULL);
INSERT INTO my_table (booking_start) VALUES (3),(7),(10);
SET #n = 9;
INSERT INTO my_table (booking_start)
SELECT #n
FROM (SELECT 1) x
LEFT
JOIN my_table y
ON y.booking_start BETWEEN #n AND #n+2
WHERE y.booking_start IS NULL
LIMIT 1;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
SELECT * FROM my_table;
+------------+---------------+
| booking_id | booking_start |
+------------+---------------+
| 1 | 3 |
| 2 | 7 |
| 3 | 10 |
+------------+---------------+
3 rows in set (0.00 sec)
-- Nothing happens, because 10 is between 9 and 11.
SET #n = 8;
INSERT INTO my_table (booking_start)
SELECT #n
FROM (SELECT 1) x
LEFT
JOIN my_table y
ON y.booking_start BETWEEN #n AND #n+2
WHERE y.booking_start IS NULL
LIMIT 1;
SELECT * FROM my_table;
+------------+---------------+
| booking_id | booking_start |
+------------+---------------+
| 1 | 3 |
| 2 | 7 |
| 3 | 10 |
+------------+---------------+
3 rows in set (0.00 sec)
-- Nothing happens, because 10 is between 8 and 10.
SET #n = 7;
INSERT INTO my_table (booking_start)
SELECT #n
FROM (SELECT 1) x
LEFT
JOIN my_table y
ON y.booking_start BETWEEN #n AND #n+2
WHERE y.booking_start IS NULL
LIMIT 1;
SELECT * FROM my_table;
+------------+---------------+
| booking_id | booking_start |
+------------+---------------+
| 1 | 3 |
| 2 | 7 |
| 3 | 10 |
+------------+---------------+
3 rows in set (0.00 sec)
-- Nothing happens, because 7 is between 7 and 9.
SET #n = 4;
INSERT INTO my_table (booking_start)
SELECT #n
FROM (SELECT 1) x
LEFT
JOIN my_table y
ON y.booking_start BETWEEN #n AND #n+2
WHERE y.booking_start IS NULL
LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM my_table;
+------------+---------------+
| booking_id | booking_start |
+------------+---------------+
| 1 | 3 |
| 2 | 7 |
| 3 | 10 |
| 4 | 4 |
+------------+---------------+
4 rows in set (0.00 sec)
-- Success, because there is no value between 4 and 6.

Related

Select rows where column > 0 after final row where column == 0

Edit 2021: I want to explain this better.
I have a table named MyTable with two columns; one column named SortColumn type Integer and one column named ExpressionColumn type Boolean.
I want to get all rows, sorted by SortColumn in ascending order, after the last row where ExpressionColumn was True.
The types are not exact.
Eg. Table with rows represented as [SortColumn,ExpressionColumn], [0:True] will get [0:True], [0:True, 1:False] will get [], [0:True, 1:False, 2:True, 3:True] will get [3:True, 4:True].
Leaving the old question below so as not to invalidate given answers. It had too many extra details.
I want to select rows after than last row where column Number is 0.
So with this table,
Id | Number
------------
0 | 5
1 | 30
2 | 10
3 | 25
I want to get rows with Id 0 to 3 inclusive.
With this table,
Id | Number
------------
0 | 5
1 | 30
2 | 10
3 | 25
4 | 0
I want to get no rows at all.
With this table,
Id | Number
------------
0 | 5
1 | 30
2 | 10
3 | 25
4 | 0
5 | 0
6 | 30
I want to get row with Id 6.
SQL details: MySQL 5.6.
Retrieve separate records:
SELECT *
FROM transaction t1
WHERE Name LIKE '%Car Wash%'
AND NOT EXISTS ( SELECT NULL
FROM transaction
WHERE t1.id <= id
AND Name LIKE '%Car Wash%' -- maybe not needed? not specified
AND Price = 0 );
Retrieve their amount only:
SELECT COUNT(*)
FROM transaction t1
WHERE Name LIKE '%Car Wash%'
AND NOT EXISTS ( SELECT NULL
FROM transaction
WHERE t1.id <= id
AND Name LIKE '%Car Wash%'
AND Price = 0 );
fiddle
you can select
select
from my_table
where name like '%car%' and price > 0
and for count
select count(*)
from (
select
from my_table
where name like '%car%' and price > 0
) t
I interpret this question as "how many rows are there for "car wash" after the first row with price > 0". If so:
select count(*)
from (select t.*,
min(case when price = 0 then id end) over () as id_at_0
from transaction t
) t
where name = 'Car Wash' and id > id_at_0
SELECT * FROM MyTable myTable1
AND NOT EXISTS ( SELECT NULL FROM MyTable
WHERE myTable1.SortColumn <= SortColumn
AND ExpressionColumn = True );

How to increment an id based on a field having a certain value going row by row

I'm importing data where groups of rows need to be given an id but there is nothing unique and common to them in the incoming data. What there is is a known indicator of the first row of a group and that the data is in order so we can step through row by row setting an id and then increment that id whenever this indicator is found. I've done this however it's incredibly slow, so is there a better way to do this in mysql or am i better off perhaps pre-processing the text data going line by line to add the id.
Example of data coming in, I need to increment an id whenever we see "NEW"
id,linetype,number,text
1,NEW,1234,sometext
2,CONTINUE,2412,anytext
3,CONTINUE,1,hello
4,NEW,2333,bla bla
5,CONTINUE,333,hello
6,NEW,1234,anything
So i'll end up with
id,linetype,number,text,group_id
1,NEW,1234,sometext,1
2,CONTINUE,2412,anytext,1
3,CONTINUE,1,hello,1
4,NEW,2333,bla bla,2
5,CONTINUE,333,hello,2
6,NEW,1234,anything,3
I've tried a stored procedure where i go row by row updating as i go, but it's super slow.
select count(*) from mytable into n;
set i=1;
while i<=n do
select linetype into l_linetype from mytable where id = i;
if l_linetype = "NEW" then
set l_id = l_id + 1;
end if;
update mytable set group_id = l_id where id = i;
end while;
No errors, it's just something that i could go line by line reading and writing the text file and do in a second while in mysql it's taking 100 seconds, it'd be nice if there was a way within mysql to do this reasonably fast so separate pre-processing was not needed.
In absence of MySQL 8+ (non availability of Windowing functions), you can use a Correlated Subquery instead:
EDIT: As pointed out by #Paul in comments,
SELECT t1.*,
(SELECT COUNT(*)
FROM your_table t2
WHERE t2.id <= t1.id
AND t2.linetype = 'NEW'
) group_id
FROM your_table t1
Above query can be more performant, if we define the following composite index (linetype, id). The order of columns is important, because we have a Range condition on id.
Previously:
SELECT t1.*,
(SELECT SUM(t2.linetype = 'NEW')
FROM your_table t2
WHERE t2.id <= t1.id
) group_id
FROM your_table t1
Above query requires indexing on id.
Another approach using User-defined Variables (Session variables) would be:
SELECT
t1.*,
#g := IF(t1.linetype = 'NEW', #g + 1, #g) AS group_id
FROM your_table t1
CROSS JOIN (SELECT #g := 0) vars
ORDER BY t1.id
It is like a looping technique, where we use Session Variables whose previous value is accessible during next row's calculation during SELECT. So, we initialize the variable #g to 0, and then compute it row by row. If we can encounter a row with NEW linetype, we increment it, else use the previous row's value. You can also check https://stackoverflow.com/a/53465139/2469308 for more discussion and caveats to take care of while using this approach.
For MySql 8.0+ you can use SUM() window function:
select *,
sum(linetype = 'NEW') over (order by id) group_id
from tablename
See the demo.
For previous versions you can simulate this functionality with the use of a variable:
set #group_id := 0;
select *,
#group_id := #group_id + (linetype = 'NEW') group_id
from tablename
order by id
See the demo.
Results:
| id | linetype | number | text | group_id |
| --- | -------- | ------ | -------- | -------- |
| 1 | NEW | 1234 | sometext | 1 |
| 2 | CONTINUE | 2412 | anytext | 1 |
| 3 | CONTINUE | 1 | hello | 1 |
| 4 | NEW | 2333 | bla bla | 2 |
| 5 | CONTINUE | 333 | hello | 2 |
| 6 | NEW | 1234 | anything | 3 |

How is COUNT(*) different to COUNT(id)? [duplicate]

I have the following query:
select column_name, count(column_name)
from table
group by column_name
having count(column_name) > 1;
What would be the difference if I replaced all calls to count(column_name) to count(*)?
This question was inspired by How do I find duplicate values in a table in Oracle?.
To clarify the accepted answer (and maybe my question), replacing count(column_name) with count(*) would return an extra row in the result that contains a null and the count of null values in the column.
count(*) counts NULLs and count(column) does not
[edit] added this code so that people can run it
create table #bla(id int,id2 int)
insert #bla values(null,null)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,null)
select count(*),count(id),count(id2)
from #bla
results
7 3 2
Another minor difference, between using * and a specific column, is that in the column case you can add the keyword DISTINCT, and restrict the count to distinct values:
select column_a, count(distinct column_b)
from table
group by column_a
having count(distinct column_b) > 1;
A further and perhaps subtle difference is that in some database implementations the count(*) is computed by looking at the indexes on the table in question rather than the actual data rows. Since no specific column is specified, there is no need to bother with the actual rows and their values (as there would be if you counted a specific column). Allowing the database to use the index data can be significantly faster than making it count "real" rows.
The explanation in the docs, helps to explain this:
COUNT(*) returns the number of items in a group, including NULL values and duplicates.
COUNT(expression) evaluates expression for each row in a group and returns the number of nonnull values.
So count(*) includes nulls, the other method doesn't.
We can use the Stack Exchange Data Explorer to illustrate the difference with a simple query. The Users table in Stack Overflow's database has columns that are often left blank, like the user's Website URL.
-- count(column_name) vs. count(*)
-- Illustrates the difference between counting a column
-- that can hold null values, a 'not null' column, and count(*)
select count(WebsiteUrl), count(Id), count(*) from Users
If you run the query above in the Data Explorer, you'll see that the count is the same for count(Id) and count(*)because the Id column doesn't allow null values. The WebsiteUrl count is much lower, though, because that column allows null.
The COUNT(*) sentence indicates SQL Server to return all the rows from a table, including NULLs.
COUNT(column_name) just retrieves the rows having a non-null value on the rows.
Please see following code for test executions SQL Server 2008:
-- Variable table
DECLARE #Table TABLE
(
CustomerId int NULL
, Name nvarchar(50) NULL
)
-- Insert some records for tests
INSERT INTO #Table VALUES( NULL, 'Pedro')
INSERT INTO #Table VALUES( 1, 'Juan')
INSERT INTO #Table VALUES( 2, 'Pablo')
INSERT INTO #Table VALUES( 3, 'Marcelo')
INSERT INTO #Table VALUES( NULL, 'Leonardo')
INSERT INTO #Table VALUES( 4, 'Ignacio')
-- Get all the collumns by indicating *
SELECT COUNT(*) AS 'AllRowsCount'
FROM #Table
-- Get only content columns ( exluce NULLs )
SELECT COUNT(CustomerId) AS 'OnlyNotNullCounts'
FROM #Table
COUNT(*) – Returns the total number of records in a table (Including NULL valued records).
COUNT(Column Name) – Returns the total number of Non-NULL records. It means that, it ignores counting NULL valued records in that particular column.
Basically the COUNT(*) function return all the rows from a table whereas COUNT(COLUMN_NAME) does not; that is it excludes null values which everyone here have also answered here.
But the most interesting part is to make queries and database optimized it is better to use COUNT(*) unless doing multiple counts or a complex query rather than COUNT(COLUMN_NAME). Otherwise, it will really lower your DB performance while dealing with a huge number of data.
Further elaborating upon the answer given by #SQLMeance and #Brannon making use of GROUP BY clause which has been mentioned by OP but not present in answer by #SQLMenace
CREATE TABLE table1 (
id INT
);
INSERT INTO table1 VALUES
(1),
(2),
(NULL),
(2),
(NULL),
(3),
(1),
(4),
(NULL),
(2);
SELECT * FROM table1;
+------+
| id |
+------+
| 1 |
| 2 |
| NULL |
| 2 |
| NULL |
| 3 |
| 1 |
| 4 |
| NULL |
| 2 |
+------+
10 rows in set (0.00 sec)
SELECT id, COUNT(*) FROM table1 GROUP BY id;
+------+----------+
| id | COUNT(*) |
+------+----------+
| 1 | 2 |
| 2 | 3 |
| NULL | 3 |
| 3 | 1 |
| 4 | 1 |
+------+----------+
5 rows in set (0.00 sec)
Here, COUNT(*) counts the number of occurrences of each type of id including NULL
SELECT id, COUNT(id) FROM table1 GROUP BY id;
+------+-----------+
| id | COUNT(id) |
+------+-----------+
| 1 | 2 |
| 2 | 3 |
| NULL | 0 |
| 3 | 1 |
| 4 | 1 |
+------+-----------+
5 rows in set (0.00 sec)
Here, COUNT(id) counts the number of occurrences of each type of id but does not count the number of occurrences of NULL
SELECT id, COUNT(DISTINCT id) FROM table1 GROUP BY id;
+------+--------------------+
| id | COUNT(DISTINCT id) |
+------+--------------------+
| NULL | 0 |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
+------+--------------------+
5 rows in set (0.00 sec)
Here, COUNT(DISTINCT id) counts the number of occurrences of each type of id only once (does not count duplicates) and also does not count the number of occurrences of NULL
It is best to use
Count(1) in place of column name or *
to count the number of rows in a table, it is faster than any format because it never go to check the column name into table exists or not
There is no difference if one column is fix in your table, if you want to use more than one column than you have to specify that how much columns you required to count......
Thanks,
As mentioned in the previous answers, Count(*) counts even the NULL columns, whereas count(Columnname) counts only if the column has values.
It's always best practice to avoid * (Select *, count *, …)

Need result set of sql Query in the order gave as input in "in" function without any creation and insertions

Need the result set of the SQL query in the order of the param values what i gave in the in function order.
mysql> select * from state where state_name in ("UP","AP","Orissa","MP");
+------------+
| state_name |
+------------+
| AP |
| MP |
| UP |
| Orissa |
+------------+
4 rows in set (0.00 sec)
Please suggest a solution that can help without using any extra insertions and creations.
select * from state where state_name in ("UP","AP","Orissa","MP")
order by FIELD(state_name ,"UP","AP","Orissa","MP")
With ANSI sql CASE
select * from state where state_name in ("UP","AP","Orissa","MP")
order by case state_name
when "UP" then 1
when "AP" then 2
when "Orissa" then 3
when "MP" then 4
else 5 end;
Old school:
SELECT
State.*
FROM State
JOIN (SELECT 'UP' sn, '1' pos UNION ALL
SELECT 'AP', '2' UNION ALL
SELECT 'Orissa', '3' UNION ALL
SELECT 'MP', '4') SortOrder
ON State.state_name = SortOrder.sn
ORDER BY SortOrder.pos;
SQL Fiddle

Update MySQL record twice using 'CASE'

I am working on an accountant software , some times the invoice contain the same item id twice (every one with its unique serial number) .. so after selling the two items I need its quantity equals quantity-1
I am using this statement for updating some records
UPDATE `table1` SET `quantity` = CASE
WHEN id = 1 THEN quantity-1
WHEN id = 1 THEN quantity-1
END
WHERE id in (1)
after updating using this statement ,the value of quantity ignores the second statement
How can I solve this ?
EDIT
The answer
Thank You ... I found the trick by myself
UPDATE `table1` SET `quantity` = CASE
WHEN id = (1*(1/1)) THEN quantity-1
WHEN id = (1*(2/2)) THEN quantity-1
WHEN id = (1*(3/3)) THEN quantity-1
END
WHERE id in (1)
the record will be updated 3 times by the same query
why I need This ?
because my software accepts that the user can add the same (Product ID) many times in the same invoice so I need the query to update the quantity many times in the same statement for one (Product ID)
I think this might actually be what you want. I have id=1 in my table 3 times, so mcount is set equal to 3. The row for ID=2 was not updated because of the where condition.
update table1
join (select id, count(*) as ct from table1 group by id) as ct_tbl
set mcount = ct_tbl.ct
where table1.id=1
mysql> select * from table1;
+------+--------+
| id | mcount |
+------+--------+
| 1 | 3 |
| 1 | 3 |
| 1 | 3 |
| 2 | 0 |
+------+--------+
4 rows in set (0.00 sec)
I don't want to hijack the other answer because it's totally correct but I think it needs more explanation.
UPDATE `table1` SET `mcount` = CASE
WHEN id = 1 THEN 1
WHEN id = 1 THEN 2
END
WHERE id in (1)
that is the equivalent of... (psuedo)
in the table table1
where the id = 1
if id = 1 then set mcount = 1
if id = 1 then set mcount = 2
Because of the where statement, we already know that we will only select rows where id=1. Those if statements will just overwrite each other. I really don't know what you're after but normally...
the following will add one to mcount
UPDATE `table1`
SET `mcount` = mcount + 1;
WHERE id in (1)
the following will toggle mcount values
UPDATE `table1`
SET `mcount` = CASE WHEN mcount = 1 THEN 2
WHEN mcount = 2 THEN 1 END
WHERE id in (1)
the following will toggle mcount values
UPDATE `table1`
SET `mcount` = CASE WHEN other_field = 'Y' THEN 1
WHEN other_field = 'N' THEN 2 END
WHERE id in (1)
working with multiple ID values...
UPDATE `table1`
SET `mcount` = CASE WHEN id = 1 THEN 1
WHEN id = 2 THEN 2
WHEN id = 3 THEN 3 END
WHERE id in (1,2,3)
which is the same as...
UPDATE `table1`
SET `mcount` = id
WHERE id in (1,2,3)
now the real question is... if ID=1 then what should mcount be?
also....
set #row:=0;
update table1
set mcount = #row:=#row+1
where id = 1;
mysql> select * from table1;
+------+--------+
| id | mcount |
+------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 0 |
+------+--------+
4 rows in set (0.00 sec)
Your statement has the same condition for each case, always will return 1 (first statement), the second statement will be ignored because it is the same condition.
Thank You ... I found the trick by myself
UPDATE `table1` SET `quantity` = CASE
WHEN id = (1*(1/1)) THEN quantity-1
WHEN id = (1*(2/2)) THEN quantity-1
WHEN id = (1*(3/3)) THEN quantity-1
END
WHERE id in (1)
the record will be updated 3 times by the same query