Getting the values of a MySQL enum using only SQL - php

In a web application, I need to populate a <select> with all possible values of a MySQL enum.
When I execute either of:
SHOW COLUMNS FROM mytable LIKE 'mycolumn';
SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'mytable' AND COLUMN_NAME = 'mycolumn';
I always end up with enum('valueA','valueB','valueC').
I know I can parse it with PHP but can it be done using SQL only?
I need something like this:
+-----------------+
| values |
+-----------------+
| valueA |
| valueB |
| valueC |
+-----------------+

This is one of Chris Komlenic's 8 Reasons Why MySQL's ENUM Data Type Is Evil:
4. Getting a list of distinct ENUM members is a pain.
A very common need is to populate a select-box or drop down list with possible values from the database. Like this:
Select color:
[ select box ]
If these values are stored in a reference table named 'colors', all you need is: SELECT * FROM colors ...which can then be parsed out to dynamically generate the drop down list. You can add or change the colors in the reference table, and your sexy order forms will automatically be updated. Awesome.
Now consider the evil ENUM: how do you extract the member list? You could query the ENUM column in your table for DISTINCT values but that will only return values that are actually used and present in the table, not necessarily all possible values. You can query INFORMATION_SCHEMA and parse them out of the query result with a scripting language, but that's unnecessarily complicated. In fact, I don't know of any elegant, purely SQL way to extract the member list of an ENUM column.

While I would agree about not using enums most of the time, it is possible in a single SQL statement:-
SELECT DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING(COLUMN_TYPE, 7, LENGTH(COLUMN_TYPE) - 8), "','", 1 + units.i + tens.i * 10) , "','", -1)
FROM INFORMATION_SCHEMA.COLUMNS
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
WHERE TABLE_NAME = 'mytable'
AND COLUMN_NAME = 'mycolumn'
This will work for enums with up to 100 possible values

This is the simplest solution
$EnumColum = $mysqli->query(
$link,
"SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'mrb_shipping_carriers' AND COLUMN_NAME = 'uspsServiceType'"
) or die("Error in " . ": " . __FILE__ . ": " . __LINE__);
$replace1 = preg_replace("/enum/", '',$EnumColum[0]['COLUMN_TYPE']);
$replace2 = preg_replace("/\(/", '',$replace1);
$replace3 = preg_replace("/\)/", '',$replace2);
$replace4 = preg_replace("/\'/", '',$replace3);
$newArray = explode(',',$replace4);
foreach($newArray as $value){
echo $value . "\n<br>";
}

Related

How to choose oldest row from the table of similar rows?

I have a table. Table has structure of id, name, color, product_id.
And the table has multiple rows with the same product_id.
With SQL query from PHP file - I would like to choose only one, the oldest, row. (The first one that was added to the current table).
What query should I use or approach?
Thank you!
Just making up a bit of mockup data ... Note the notes I put in. And I trust it's a newer version of MySQL, as the older ones did not support ROW_NUMBER() OVER() .
Here goes:
WITH
-- input ... you *need* a timestamp to identify the oldest ---
indata(id, name, color, product_id,ts) AS (
SELECT 1,'Arthur','blue' ,42,TIMESTAMP'2021-01-31 17:45:00'
UNION ALL SELECT 1,'Arthur','blue' ,42,TIMESTAMP'2021-01-31 17:50:00'
UNION ALL SELECT 1,'Arthur','blue' ,42,TIMESTAMP'2021-01-31 17:55:00'
UNION ALL SELECT 1,'Arthur','blue' ,42,TIMESTAMP'2021-01-31 18:00:00'
UNION ALL SELECT 2,'Ford' ,'red' ,42,TIMESTAMP'2021-01-31 17:45:00'
UNION ALL SELECT 2,'Ford' ,'blue', 42,TIMESTAMP'2021-01-31 17:50:00'
UNION ALL SELECT 2,'Ford' ,'green',42,TIMESTAMP'2021-01-31 17:55:00'
UNION ALL SELECT 2,'Ford' ,'cyan' ,42,TIMESTAMP'2021-01-31 18:00:00'
)
,
-- select all, plus a rank, on which you will filter outside ..
with_rank AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY id ORDER BY ts) AS rnk
FROM indata
)
SELECT
id
, name
, color
, product_id
, ts
FROM with_rank
WHERE rnk = 1
id|name |color|product_id|ts
1|Arthur|blue |42 |2021-01-31 17:45:00
2|Ford |red |42 |2021-01-31 17:45:00
One method is a correlated subquery:
select t.*
from t
where t.id = (select min(t2.id)
from t t2
where t2.product_id = t.product_id
);
This assumes that id is incrementing with each insertion. If not, you have no way of knowing what the "oldest" row is. SQL tables represent unordered sets, so there is no "oldest" row unless a column contains that information.
SELECT * FROM TableName WHERE product_id = ProductID ORDER BY product_id LIMIT 1;

Return data from array inside 'IN(....)' of SELECT query

I have the following query:
SELECT question_code FROM table_questions WHERE question_code IN (1,2,3,111,222);
Here, values (1,2,3,111,222) are coming from PHP array.
The output for the above query is:
question_code
1
2
3
I want the output to be the question_codes which are not in the table and present in the Array.
i.e. I want the output to be question_code which do not exist in the table.
question_code
111
222
I know this problem can be handled in PHP after retrieving the data from the Table. But as I may have large number of tuples, solution which can take care of this thing at query level would be helpful.
You will get the record of SELECT question_code FROM table_questions WHERE question_code IN (1,2,3,111,222); in array and values (1,2,3,111,222) are coming from PHP array.
Use array_diff to compare two arrays and gets the difference.
For reference http://www.w3schools.com/php/func_array_diff.asp
If you want to use plain SQL you have to use a LEFT JOIN:
SELECT c.question_code
FROM (SELECT 1 AS question_code
UNION ALL select 2
UNION ALL SELECT 3
UNION ALL SELECT 111
UNION ALL SELECT 222) AS c
LEFT JOIN table_questions q
ON c.question_code = q.question_code
WHERE
q.question_code IS NULL
you can create the subquery SELECT .. UNION ALL .. dynamically, like this:
<?php
$ids = array(1,2,3);
$subquery = str_repeat('SELECT ? AS q UNION ALL ', count($ids) - 1) . 'SELECT ? AS q';
$sql = <<<SQL
SELECT c.q FROM ($subquery) AS c
LEFT JOIN table_questions q
ON c.q = q.question_code
WHERE
q.question_code IS NULL
SQL;
$stm = $db->prepare($sql);
$stm->execute($ids);
$data = $stm->fetchAll();
?>
but it might not be too pratical... the only alternative is to process the returned question_codes codes in php.

execute long lasting php code with connection to database

I have to execute a PHP code just one time to make some changes in my database to have better tables. The code is too heavy and send more than 190000 queries to database (both select and insert).
Now the problem is when I execute it on the browser, form the result on the database and words that I've set to echo to check if it is going in correct way, I can find that it is executed partly. No error/warning/notice appears on the page.
I set max execute time and all the related settings to 1hour but it just take 30minuts and stop the for loop at once.
this is a sample of a part of my code:
for ($i=9;$i<=19909; $i++){
$j=0;
$select= "select synonym from synonym where x_=". $i ."";
//echo $select;
//echo "</br>";
$select_result= mysqli_query($connection,$select);
if (mysqli_num_rows($select_result)>0) {
$row = mysqli_fetch_assoc($select_result);
$arra = explode("،",$row["synonym"]);
}
while ($arra[$j]){
$text=trim($arra[$j]);
$search="select word from semiresult where word='". $text ."'";
$search_result= mysqli_query($connection,$search);
if (mysqli_num_rows($search_result)==0) {
$insert="insert into semiresult (x, word, synonym) values ('','". $text."','".$syn."')";
}elseif (mysqli_num_rows($search_result)>0){
$repeat="UPDATE semiresult SET synonym=CONCAT(synonym,'".$syn2."') where word like'".$arra[$j]."'";
$repeat_result=mysqli_query($connection,$repeat) or die ('request "Could not execute SQL query" '.$repeat);
}
With work you could avoid doing much of this in php. Whether it is worthwhile would depend on the data and table declares.
For example, assuming that word has a unique index on it in the semiresult table and that the synonym field has at most 1000 delimited fields you could do something like this (untested):-
INSERT INTO semiresult (x, word, synonym)
SELECT '',
'". $text."',
SUBSRTING_INDEX(SUBSRTING_INDEX(t1.synonym, '،', (hundreds.aCnt * 100 + tens.aCnt * 10 + units.aCnt + 1)), '،', -1)
FROM synonym t1
CROSS JOIN
(
SELECT 0 aCnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) AS units
CROSS JOIN
(
SELECT 0 aCnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) AS tens
CROSS JOIN
(
SELECT 0 aCnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) AS hundreds
WHERE t1.x_ BETWEEN 9 AND 19909
AND LENGTH(t1.synonym) - LENGTH(REPLACE(t1.synonym, '،', '')) > (hundreds.aCnt * 100 + tens.aCnt * 10 + units.aCnt)
ON DUPLICATE KEY UPDATE synonym=CONCAT(synonym, VALUES(synonym))
after all I solve it!!
After changing time limitations like max execute time and any other time limitations to two hours in php settings I add this line of code at the begining of my code then it execute until the end of the code.
set_time_limit(0);
This code was planned to execute one time only otherwise I have not do this changes.

How to make a where clause where the value is an array from a previous sql query

I have a small list of data which is from an SQL database and uses mysql_fetch_array($query_run) to get it. This all works fine and I can echo out that data to double check it.
But where I want to use it is another SQL query, but where it doesn't equal it. My code at the moment only doesn't include one of the data items. So, I'm guessing you have to do something to let it know it's an array not just one piece of data?
Here's my code:
$query = "SELECT * FROM myFriends WHERE idPerson = '$loggedInUserId'";
$query_run = mysql_query($query);
while($row = mysql_fetch_array($query_run)) {
$idfriend = $row['idFriend'];
$queryFrirend = "SELECT * FROM perosn WHERE idPerson != '$idfriend'
Any help would be great!
Personally, I wouldn't bother with turning an array into a WHERE clause, if the set is returned from a MySQL query. Relational databases are built for this kind of thing.
A query with the familiar anti-join pattern will return the specified result:
SELECT p.*
FROM person p
LEFT
JOIN myFriends f
ON f.idFriend = p.idPerson
AND f.idPerson = '$loggedInUserId'
WHERE f.idFriend IS NULL
This says get all rows from the person table, and match to rows from the myFriends table. The WHERE clause says to exclude rows where a matching row was found, leaving only rows from person that didn't have a matching row returned from myFriends. With appropriate indexes, MySQL can blaze through that, without the overhead of sending a list of id values in a WHERE clause.
But, that doesn't really answer the question you asked.
The SQL you specified, including a list of idPerson values to be excluded, can be of several forms:
SELECT p.*
FROM person p
WHERE p.idPerson NOT IN (2,3,5,7,11,13,15,17,19)
or
SELECT p.*
FROM person p
WHERE p.idPerson <> 2
AND p.idPerson <> 3
AND p.idPerson <> 5
AND p.idPerson <> 7
AND p.idPerson <> 11
AND p.idPerson <> 13
AND p.idPerson <> 15
AND p.idPerson <> 17
or
SELECT p.*
FROM person p
WHERE NOT EXISTS
( SELECT 1
FROM ( SELECT 2 AS idPerson
UNION ALL SELECT 3
UNION ALL SELECT 5
UNION ALL SELECT 7
UNION ALL SELECT 9
UNION ALL SELECT 11
UNION ALL SELECT 13
UNION ALL SELECT 15
UNION ALL SELECT 17
UNION ALL SELECT 19
) f
WHERE f.idPerson = p.idPerson
)
or
SELECT p.*
FROM person p
LEFT
JOIN ( SELECT 2 AS idPerson
UNION ALL SELECT 3
UNION ALL SELECT 5
UNION ALL SELECT 7
UNION ALL SELECT 9
UNION ALL SELECT 11
UNION ALL SELECT 13
UNION ALL SELECT 15
UNION ALL SELECT 17
UNION ALL SELECT 19
) f
ON f.idPerson = p.idPerson
WHERE f.idPerson IS NULL
It's just a matter of looping through the rows in the result set, and using that value to format an appropriate SQL statement. The easiest approach (apart from aforementioned avoidance of having to run two separate queries to get the resultset you want), would be to build an array of the friendId column values. And then turn that array into a string of comma separated literals 2,3,5,7,9,11,13,17,19. Of course, you'd need to handle the edge case of no rows returned, because foo NOT IN () isn't valid SQL syntax.
If you want to build the statement on the fly, as you loop through the rows, you could do something like this:
$queryFrirend = "SELECT * FROM person WHERE 1=1";
while($row = mysql_fetch_array($query_run)) {
$queryFrirend .= " AND idPerson <> " . (int) $row['idFriend'];
}
$queryFrirend .= " ORDER BY idPerson";
echo "SQL=", $queryFrirend;
Though that's going to some ugly SQL. (I don't want to be around when the DBA comes hunting for you.)
You can't use arrays in SQL queries. You need to turn the array into a String of values separated by commas, and use IN().
SELECT * FROM perosn WHERE idPerson Not IN ('person1', 'person2')

Three columns - get MAX with SQL

I have table:
Rating:
id | one | two | three
1 | 12 | 3 | 7
2 | 11 | 30 | 3
3 | 8 | 14 | 4
How can i get with SQL MAX values from these fields (one, two, three)? For this example this is 30.
In MySQL you can use the GREATEST Function:
SELECT MAX(GREATEST(one, two, three))
FROM T;
Example on SQL Fiddle
SELECT MAX(field) FROM (
SELECT one AS field FROM table
UNION
SELECT two AS field FROM table
UNION
SELECT three AS field FROM table
) AS t
select GREATEST(max(one), max(two), max(three)) as maximum
from table;
You can unpivot the data similar to this:
select max(value)
from
(
select id, 'one' col, one value
from yourtable
union all
select id, 'two' col, two value
from yourtable
union all
select id, 'three' col, three value
from yourtable
) src
See SQL Fiddle with Demo.
Or you can use something like this:
SELECT max(data)
FROM
(
SELECT
CASE s.col
WHEN 'one' THEN one
WHEN 'two' THEN two
WHEN 'three' THEN three
END AS DATA
FROM yourtable t
CROSS JOIN
(
SELECT 'one' AS col
UNION ALL SELECT 'two'
UNION ALL SELECT 'three'
) s
) s
See SQL Fiddle with Demo
Try this query
SELECT tempTable.id, tempTable.max(val)
FROM (SELECT id, max(one) AS val
FROM tbl
UNION
SELECT id, max(two) AS val
FROM tbl
UNION
SELECT id, max(three) AS val
FROM tbl
) AS tempTable;
select max( if( one > two, if( one > three, one, three ), if( two > three, two, three ) )
from Rating
You can use case statement
SELECT
CASE
WHEN one >= two AND one >= three THEN one
WHEN two >= one AND two >= three THEN two
WHEN three >= one AND three >= two THEN three
ELSE one
END AS MaxVal

Categories