SQL SELECT Query for Multiple Values and Ranges in Column - mysql

SQL SELECT query for multiple values and ranges in row data of a given column.
Problem description:
Server: MySQL
Database: Customer
Table: Lan
Column: Allowed VLAN (in the range 1-4096)
One row has data as below in the column Allowed VLAN:
180,181,200,250-499,550-811,826-mismatched
I need a SELECT statement WHERE the column Allowed VLAN includes a given number for instance '600'. The given number '600' is even one of the comma separated value or included in any of the ranges "250-499","550-811" or it is just the starting number value of "826-mismatched" range.
SELECT * WHERE `Allowed VLAN`='600' OR `Allowed VLAN` LIKE '%600%' OR (`Allowed VLAN` BETWEEN '1-1' AND '1-4096');
I could not figure it out how to deal with data ranges with WHERE Clause. I have solved the problem with PHP code using explode() split functions etc., but I think there are some SQL SELECT solutions.
I would be appreciated for any help.

I would highly recommend normalizing your data. Storing a comma-separated list of items in a single row is generally never a good idea.
Assuming you can make such a change, then something like this should work for you (although you could consider storing your ranges in different columns to make it even easier):
create table lan (allowedvan varchar(100));
insert into lan values
('180'),('181'),('200'),('250-499'),('550-811'),('826-mismatched');
select *
from lan
where allowedvan = '600'
or
(instr(allowedvan,'-') > 0 and
'600' >= left(allowedvan,instr(allowedvan,'-')-1) and
'600' <= right(allowedvan,length(allowedvan)-instr(allowedvan,'-'))
)
SQL Fiddle Demo
This uses INSTR to determine if the value contains a range (a hyphen) and then uses LEFT and RIGHT to get the range. Do not use LIKE because that could return inaccurate results (600 is like 1600 for example).
If you are unable to alter your database, then perhaps look into using a split function (several posts on it on SO) and then you can do something similar to the above method.

Related

MySQL LEAST() with arbitrary number of parameters; longest match in a table

I would like to create a MySQL query to find the longest match (of a given ip address in quad-dotted format) that is present in a table of subnets.
Ultimately, I'd like to create a LEFT JOIN that will display every quad-dotted ip address in one table joined with their longest matches in another table. I don't want to create any temporary tables or structure it as a nested query.
I'm somewhat of a MySQL newbie, but what I'm thinking is something like this:
SELECT `ip_address`
LEFT JOIN ON
SELECT `subnet_id`
FROM `subnets_table`
WHERE (`maximum_ip_value` - `minimum_ip_value`) =
LEAST(<list of subnet intervals>)
WHERE INET_ATON(<given ip address>) > `minimum_ip_value`
AND INET_ATON(<given ip address>) < `maximum_ip_value`;
Such that minimum_ip_value and maximum_ip_value are the lowest and highest decimal-formatted ip addresses possible in a given subnet-- e.g., for the subnet 172.16.0.0/16:
minimum_ip_value = 2886729728 (or 172.16.0.0)
maximum_ip_value = 2886795263 (or 172.16.255.255)
And <list of subnet intervals> contains all intervals in subnets_table where <given ip address> is between minimum_ip_value and maximum_ip_value
And if more than one interval contains <given ip address>, then the smallest interval (i.e., smallest subnet, or most specific and "longest" match) is joined.
Ultimately, all I really want is the subnet_id value that corresponds with that interval.
So my questions are:
1) Can I use the LEAST() function with an arbitrary number of parameters? I'd like to compare every row of subnets_table, or more specifically, every row's interval between minimum_ip_value and maximum_ip_value, and select the smallest interval.
2) Can I perform all of this computation within a LEFT JOIN query? I'm fine with any suggestions that will be fast, encapsulated, and avoid repetitive queries of the same data.
I'm wondering if this is even possible to perform in a single query (i.e., without querying the subnets table for each ip address), but I don't know enough to rule it out. Please advise if this looks like it won't work, so I can try another angle.
Thanks.
After some research and trial & error, I see that there are a few issues with the prototype query above:
The LEAST() function takes only a set number of arguments. As per my original question, I want a function that will work on an arbitrary number of arguments, or every row in a table. That is a different function in MySQL, MIN().
The function MIN() has a lower precedence than the JOIN functions in MySQL, and is evaluated after the JOIN functions in any given query. Therefore, I can't JOIN on the MIN() of a set of values, because the MIN() doesn't exist yet at the time the JOIN is performed.
The only way I could see to solve this issue was to perform two separate queries: one with the MIN(), performed first, and another with the JOIN, performed on the results of the first query. This meant that for a table with n rows, I'd perform n^n queries, instead of n queries. That wasn't acceptable.
To work around the issue, I wrote a new script that modifies the database before any of these queries are ever performed. Each subnet is given its own "bucket" of ip values, and all values in that range map to that subnet. If a more specific (i.e., smaller) subnet overlaps a less specific (i.e., larger) subnet, then the more specific range is mapped only to the smaller subnet, and the larger subnet retains only the values from the less specific range. Now any given ip address falls into only one "bucket", and maps to only one subnet, which is its most specific match. I can JOIN on this match and never have to worry about the MIN() function.

Mysql: query not giving accurate result with IN clause and inner query

I'm trying to get zip codes from zip_id's which are internally stored in companies service table below screens will give you clear idea
I have wrote this query
companies service table
Please suggest me your valuable views . Thanks in advance.
As already mentioned your database scheme is not very well designed, it violates even 1st normal form. You'd need another table where you'd store serv_area_id and zip_code (with possibly multiple rows for a signle serv_area_id) and search within this table and eventually join your original table.
Nevertheless, in order to get the result you describe you cannot use the IN operator as it operates on a value and multiple values in a form of table (either explicit via nested SELECT or enumeration literal (val1, ..., valN)). I would try some string matching as illustrated below. However, consider it rather an ugly hack than correct solution(!)
SELECT zip FROM cities_extended WHERE (
SELECT GROUP_CONCAT(',', serv_are_zipcodes)
FROM company_service_areas WHERE ...
) LIKE concat('%(', id, ')%')

How to get the fifth field of the second register of a table?

I have an automatically generated SQL database.
I don't know the name of the fields, and I don't know the value of the fields; I just know which number of register I need to get and with number of field of that register.
For example, if I need to obtain the fifth field of the second register of the table "Table1" of the database, which SQL query should I do?
Rows in a table in a database are formally unordered, though they are, of course, stored in some order. There's no way in SQL to refer to columns in a table by position; you must know the name of the column.
Since you know the table name, you can interrogate the system catalog to learn the columns in the table, and therefore the second column name in the table (assuming it isn't a single-column table).
However, if you don't know the schema of the tables, you can't do anything meaningful in the way of querying the data. You have to know what the columns mean to know what the query is going to do.
Clearly you can run some query on the table (once you know the column name you're after) and then collect two rows of data; the second row is the one you're after.
...
There's a half-cheat that you can use which will work if your database access language returns you rows with the values for each row in an array - as in Perl with DBI, or PHP, or ...
SELECT * FROM Table1;
This will collect all the data (including column 5, assuming there are that many columns), and your fetch operation may return the values represented by * into an array, and you can then look at the value in the fifth element of the array for the second row to see the data. In many SQL DBMS (I don't know about MySQL specifically), you can even use an obsolescent notation to order by the fifth column:
SELECT * FROM Table1 ORDER BY 5;
The 5 here refers to the fifth column in the result set which, given that this is selecting all columns from a single table, means the fifth column of the table.
However, running blind like that is a ridiculous proposition for the long term. You must understand the schema and its interpretation to be able to use a database sensibly.
You could try:
SELECT *
FROM information_schema.COLUMNS
WHERE information_schema.COLUMNS.TABLE_SCHEMA = '<DATABASENAME>'
AND information_schema.COLUMNS.TABLE_NAME = '<TABLENAME>'
ORDER BY information_schema.COLUMNS.ORDINAL_POSITION ASC
This would give you the table metadata, including column names and types.
can you not do it thru PHP (or your choice):
$i=1;
while($row = mysql_fetch_row($res))
{
if ($i == 2)
{
echo $row[4];
}
$i++;
}
Presumably you have access to the database?
Can't you do:
SHOW CREATE TABLE Table1;
The order of the columns returned should give you the names of the fields which you can then use in a query.

remove duplicates in mysql database

I have a table with columns latitude and longitude. In most cases the value extends past the decimal quite a bit: -81.7770051972473 on the rare occasion the value is like this: -81.77 for some records.
How do I find duplicates and remove one of the duplicates for only the records that extend beyond two decimal places?
Using some creative substring, float, and charindex logic, I came up with this:
delete l1
from
latlong l1
inner join (
select
id,
substring(cast(latitude as varchar), 0, INSTR(CAST(latitude as varchar))+3, '.') as truncatedLat
from
latlong
) l2 on
l1.id <> l2.id
and l1.latitude = cast(l2.truncatedLat as float)
Before running, try select * in lieu of delete l1 first to make sure you're deleting the right rows.
I should note that this worked on SQL Server using functions I know exist in MySQL, but I wasn't able to test it against a MySQL instance, so there may be some little tweaking that needs to be done. For example, in SQL Server, I used charindex instead of instr, but both should work similarly.
Not sure how to do that purely in SQL.
I have used scripting languages like PHP or CFML to solve similar needs by building a query to pull the records then looping over the record set and performing some comparison. If true, then VERY CAREFULLY call another function, passing in the record ID and delete the record. I would probably even leave the record in the table, but mark some another column as isDeleted.
If you are more ambitious than I, it looks like this thread is close to what you want
Deleting Duplicates in MySQL
finding multi column duplicates mysql
Using an external programming language (Perl, PHP, Java, Assembly...):
Select * from database
For each row, select * from database where newLat >= round(oldLat,2) and newLat < round(oldLat,2) + .01 and //same criteria for longitude
Keep one of them based on whatever criteria you choose. If lowest primary key, sort by that and skip the first result.
Delete everything else.
Repeat skipping to this step for any records you already deleted.
If for some reason you want to identify everything with greater than 2 digit precision:
select * from database where lat != round(lat,2), or long != round(long,2)

SQL Select Query with single field as formatted and remaining fields as wildcard

I'm trying to fetch records from a table in MySQL 5.5 (Community Server) which has 22 fields in total, the first field is of type DATETIME, and I want that field formatted with DATE_FORMAT() method, while I want to fetch remaining fields as they are. So basically what I'm trying to do is something like this
SELECT DATE_FORMAT(fdate,'%r %d-%m-%Y'),* FROM userdetails;
While I know this is invalid syntax, and currently I'm accomplishing this by selecting all the remaining fields manually which obviously makes the query longer. So is there a more elegant way to do the same? I also thought to use two different queries where 1st will fetch the formatted date and second will be usual
SELECT * FROM userdetails;
But since I fetch records sorted by date field itself, I guess using two different queries may lead to conflicts on order of records returned.
You can use
SELECT DATE_FORMAT(fdate,'%r %d-%m-%Y'), ud.*
FROM userdetails ud
which will select all columns (including the original fdate) along with the formatted value.
There is no way to select "almost all columns" except listing them explicitly.
Why don't you do the date formatting in the application code? Let the database handle the data and let the application handle presentation.
In MySQL you can reverse it and put the * first and then columns you want to specify. You'll just get two fdate columns. If you're using hashes in perl or associative arrays in PHP, or the equivalent in other languages, to fetch the rows then you'd end up having that index overwritten.
Best way to deal with it is to name the column:
SELECT *, DATE_FORMAT(fdate,'%r %d-%m-%Y') AS formatted_date FROM userdetails;
Now you essentially have two date columns, you just used the named one when you want to use your date.
DATA_FORMAT(fdate,'%r %d-%m-%Y').....
wont work
DATE_FORMAT(fdate,'%r %d-%m-%Y')....
SELECT DATE_FORMAT(fdate,'%r %d-%m-%Y') AS `formattedDate` , * FROM userdetails;
That should do the trick. Can't se why your first effort fails, you should check the field names it returned - suspect you'll find one called DATE_FORMAT( ....