SQL, build a query using data provided in the query itself - mysql

For experimental purposes only.
I would like to build a query but not querying data extracted for any table but querying data provided in the query it self. Like:
select numbers.* from (1, 2, 3) as numbers;
or
select numbers.* from (field1 = 1, field2 = 2, field3 = 3) as numbers;
so I can do things like
select
numbers.*
from (field1 = 1, field2 = 2, field3 = 3) as numbers
where numbers.field1 > 1;
If the solution is specific for a database engine could be interesting too.

If you wanted the values to be on separate rows instead of three fields of the same row, the method is the same, just one row per value linked with a union all.
select *
from(
select 1 as FieldName union all
select 2 union all
select 3 union all
select 4 union all -- we could continue this for a long time
select 5 -- the end
) as x;
select numbers.*
from(
select 1 ,2, 3
union select 3, 4, 5
union select 6, 7, 8
union select 9, 10, 11 -- we could continue this for a long time
union select 12, 13, 14 -- the end
) as numbers;
This works with MySQL and Postgres (and most others as well).
[Edit] Use union all rather than just union as you do not need to remove duplicates from a list of constants. Give the field(s) in the first select a meaningful name. Otherwise, you can't specify a specific field later on: where x.FieldName = 3.
If you don't provide meaningful names for the fields (as in the second example), the system (at least MySQL where this was tested) will assign the name "1" for the first field, "2" as the second and so on. So, if you want to specify one of the fields, you have to write expressions like this:
where numbers.1 = 3

Use the values row constructor:
select *
from (values (1),(2),(3)) as numbers(nr);
or using a CTE.
with numbers (nr) as (
values (1),(2),(3)
)
select *
from numbers
where nr > 2;
Edit: I just noticed that you also taggeg your question with mysql: the above will not work with MySQL, only with Postgres (and a few other DBMS)

You can use a subquery without table like so:
SELECT
numbers.*
FROM (
SELECT
1 AS a,
2 AS b,
3 AS c
UNION
SELECT
4,
5,
6
) AS numbers
WHERE
numbers.a > 1
If you like queries to always have a table referenced there is a Psuedo table that always has 1 row and no columns called DUAL, you can use it like so:
SELECT
numbers.*
FROM (
SELECT
1 AS a,
2 AS b,
3 AS c
FROM
DUAL
UNION
SELECT
4,
5,
6
FROM
DUAL
) AS numbers
WHERE
numbers.a > 1

Related

MySQL: Count rows with similar not duplicated content as one

I am working with Codeigniter and its Query Builder class where I have a table with IDs and names.
Those names look like 1234_1a or 2345_2a where 1a can be 1b or 2a,2b,3a... and so on.
Now I want to count all these "1234" and "2345" but write them as one type.
So far I tried with:
$this->db->like('names', '1a', 'before');
$this->db->or_like('names', '1b', 'before');
return $this->db->count_all_results('table');
But the problem:
What if there is 3456_2a but no 3456_1a, than it doesn't work anymore...
id name
2 1212_1a
3 1243_1a
7 3142_1a
24 1243_2a
30 3142_2b
80 2315_2b
136 1243_3b
512 8562_1a
This is how I would like it:
Result:
name count
1212 1
1243 1
3142 1
2315 1
8562 1
If we always want to return a count value of 1, when the count of the number of rows in more than 1 ... then we aren't really returning a count.
And what is the pattern of the names... do they end with a digit and a letter, or is that underscore character important too?
What is to be done with name values such as 12345a or 5678_b2 or 11_22_3b? How are those to be handled?
Seems to me like we want to use a SQL query like this:
SELECT SUBSTRING_INDEX(t.name,'_',1) AS `foo`
, 1 AS `count`
FROM (
SELECT 2 AS `id`, '1212_1a' AS `name`
UNION ALL SELECT 3, '1243_1a'
UNION ALL SELECT 7, '3142_1a'
UNION ALL SELECT 24, '1243_2a'
UNION ALL SELECT 30, '3142_2b'
UNION ALL SELECT 80, '2315_2b'
UNION ALL SELECT 136, '1243_3b'
UNION ALL SELECT 512, '8562_1a'
) t
GROUP BY `foo`
ORDER BY `foo`
The inline view (derived table) is in the query for testing. Replace that with the table reference:
SELECT SUBSTRING_INDEX(t.name,'_',1) AS `foo`
, 1 AS `count`
FROM mytable t
GROUP BY `foo`
ORDER BY `foo`
The expression for foo may need to be adjusted, to get desirable behavior with values that don't follow the regular pattern. Consider name values with no underscore, with more than one underscore, with endings other than a digit. We could also include a WHERE clause to exclude rows that don't follow the pattern,
WHERE t.name REGEXP '_[0-9][a-z]$'
(only name values that end with underscore, digit, lowercase letter).
Without a tested SQL query, I wouldn't know what to implement in CodeIgniter Query Builder.

SQL - MYSQL One query recieving set limit on different column values

Lets say i have a table with a set of different columns (offcourse),
Example :
Table
id
col1 INTEGER(1), // 0 || 1
col2 // --||--
col3 // --||--
col4 // --||--
Is it possible, in 1 query to select 4 rows where col1=1 and then select 4 rows where col2=1 and then select 4 rows where col3=1 etc etc. I think you understand what i mean.
What i have done so far is to make 4 different queries OR make one query and just do a (col1 = 1 OR col2=1 OR... etc).
This works but if i limit that result to lets say 16, then i might get 15 rows with col1=1 and maybe 1 row with col2=1 and then col3,col4 - no result.
So dear fellas; is there a way to do this in 1 query (i think not)
Select * from table where col1=1 limit 4
Union
Select * from table where col2=1 limit 4
Union
Select * from table where col3=1 limit 4
Union
Select * from table where col4=1 limit 4
This will get you one result with 16records max. If there are less then 4rows for a certin criteria, you'll get less rows. Duplicates will be removed, resulting in less then 16 rows. Not different rows.
If for a single row col1=1 and col2=1, then it might be returned twice if you use union all, but just union is slower with large datasets
I think you are looking for the UNION ALL construct. You may simply write
SELECT * FROM tab WHERE col1 = 1
UNION ALL
SELECT * FROM tab WHERE col2 = 2
UNION ALL
SELECT * FROM tab WHERE col3 = 3
and so on. Just be aware of a fact that the rows may be duplicated in the result (if they satisfy more criteria from the conditions).

What would be the SQL query/command to find length of numeric field from a table?

I want to know how we can find of length of Numeric field in sql. What will be the command / Query so that we can find length of perticular field in table.
E.g.:
For below table:
Column1 Column2
1 1111
2 11
3 44444
4 11
5 111
From above example I want to see Record/fields in column "column2" which has numeric length '2'.
What query should I execute to achieve this?
You can also use the POWER function and < and >= to maintain SARGability
WITH tbl (column1, column2) AS(
SELECT 1, 1111 UNION ALL
SELECT 2, 11 UNION ALL
SELECT 3, 44444 UNION ALL
SELECT 4, 11 UNION ALL
SELECT 5, 111
)
SELECT *
FROM tbl
WHERE
column2 < POWER(10, 2)
AND column2 >= POWER(10, 1)
Use LEN function
select *
from table
where len(column2) = 2
This will not work when you want to include data like '0.2' or similar values
You can't as stated here
Length and digits are both properties of a physical representation of a number in a specific base, i.e. a String.
So you have to convert numeric field to varchar/char first then find the length as follows:
SQL Server:
select *
from table
where len(CONVERT (varchar(10), colunm2 )) = 2
MySQL:
select *
from table
where length(convert(column2,char)) = 2

UNNEST function in MYSQL like POSTGRESQL

Is there a function like "unnest" from POSTGRESQL on MYSQL?
Query (PSQL):
select unnest('{1,2,3,4}'::int[])
Result (as table):
int |
_____|
1 |
_____|
2 |
_____|
3 |
_____|
4 |
_____|
Short answer
Yes, it is possible. From technical viewpoint, you can achieve that with one query. But the thing is - most probably, you are trying to pass some logic from application to data storage. Data storage is intended to store data, not to represent/format it or, even more, apply some logic to it.
Yes, MySQL doesn't have arrays data type, but in most cases it won't be a problem and architecture can be created so it will fit those limitations. And in any case, even if you'll achieve it somehow (like - see below) - you won't be possible to properly work later with that data, since it will be just result set. You may store it, of course - so to, let's say, index later, but then it's again a task for an application - so to create that import.
Also, make sure that it is not a Jaywalker case, so not about storing delimiter-separated values and later trying to extract them.
Long answer
From technical viewpoint, you can do it with Cartesian product of the two row sets. Then use a well known formula:
N = d1x101 + d2x102 + ...
Thus, you'll be able to create a "all-numbers" table and later iterate through it. That iteration, together with MySQL string functions, may lead you to something like this:
SELECT
data
FROM (
SELECT
#next:=LOCATE(#separator,#search, #current+1) AS next,
SUBSTR(SUBSTR(#search, #current, #next-#current), #length+1) AS data,
#next:=IF(#next, #next, NULL) AS marker,
#current:=#next AS current
FROM
(SELECT 0 as i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as n1
CROSS JOIN
(SELECT 0 as i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as n2
CROSS JOIN
(SELECT
-- set your separator here:
#separator := ',',
-- set your string here:
#data := '1,25,42,71',
-- and do not touch here:
#current := 1,
#search := CONCAT(#separator, #data, #separator),
#length := CHAR_LENGTH(#separator)) AS init
) AS joins
WHERE
marker IS NOT NULL
The corresponding fiddle would be here.
You should also notice: this is not a function. And with functions (I mean, user-defined with CREATE FUNCTION statement) it's impossible to get result row set since function in MySQL can not return result set by definition. However, it's not true to say that it's completely impossible to perform requested transformation with MySQL.
But remember: if you are able to do something, that doesn't mean you should do it.
This sample fetchs all "catchwords" from Table data, wich are seperated by ","
Maximum values in the commaseparated list is 100
WITH RECURSIVE num (n) AS (
SELECT 1
UNION ALL
SELECT n+1 FROM num WHERE n<100 -- change this, if more than 100 elements
)
SELECT DISTINCT substring_index(substring_index(catchwords, ',', n), ',', -1) as value
FROM data
JOIN num
ON char_length(catchwords) - char_length(replace(catchwords, ',', '')) >= n - 1
In newer Version of MySQL/MariaDB you can use JSON_TABLE if you can JOIN the elements:
SELECT cat.catchword, dat.*
FROM data dat
CROSS JOIN json_table(concat('[',dat.catchwords, ']')
, '$[*]' COLUMNS(
catchword VARCHAR(50) PATH '$'
)
) AS words

mysql distribution of combinations/values

I have a mysql table which contains some random combination of numbers. For simplicity take the following table as example:
index|n1|n2|n3
1 1 2 3
2 4 10 32
3 3 10 4
4 35 1 2
5 27 1 3
etc
What I want to find out is the number of times a combination has occured in the table. For instance, how many times has the combination of 4 10 or 1 2 or 1 2 3 or 3 10 4 etc occured.
Do I have to create another table that contains all possible combinations and do comparison from there or is there another way to do this?
For a single combination, this is easy:
SELECT COUNT(*)
FROM my_table
WHERE n1 = 3 AND n2 = 10 AND n3 = 4
If you want to do this with multiple combinations, you could create a (temporary) table of them and join that table with you data, something like this:
CREATE TEMPORARY TABLE combinations (
id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
n1 INTEGER, n2 INTEGER, n3 INTEGER
);
INSERT INTO combinations (n1, n2, n3) VALUES
(1, 2, NULL), (4, 10, NULL), (1, 2, 3), (3, 10, 4);
SELECT c.n1, c.n2, c.n3, COUNT(t.id) AS num
FROM combinations AS c
LEFT JOIN my_table AS t
ON (c.n1 = t.n1 OR c.n1 IS NULL)
AND (c.n2 = t.n2 OR c.n2 IS NULL)
AND (c.n3 = t.n3 OR c.n3 IS NULL)
GROUP BY c.id;
(demo on SQLize)
Note that this query as written is not very efficient due to the OR c.n? IS NULL clauses, which MySQL isn't smart enough to optimize. If all your combinations contain the same number of terms, you can leave those out, which will allow the query to make use of indexes on the data table.
Ps. With the query above, the combination (1, 2, NULL) won't match (35, 1, 2). However, (NULL, 1, 2) will, so, if you want both, a simple workaround would be to just include both patterns in your table of combinations.
If you actually have many more columns than shown in your example, and you want to match patterns that occur in any set of consecutive columns, then your really should pack your columns into a string and use a LIKE or REGEXP query. For example, if you concatenate all your data columns into a comma-separated string in a column named data, you could search it like this:
INSERT INTO combinations (pattern) VALUES
('1,2'), ('4,10'), ('1,2,3'), ('3,10,4'), ('7,8,9');
SELECT c.pattern, COUNT(t.id) AS num
FROM combinations AS c
LEFT JOIN my_table AS t
ON CONCAT(',', t.data, ',') LIKE CONCAT('%,', c.pattern, ',%')
GROUP BY c.id;
(demo on SQLize)
You could make this query somewhat faster by making the prefixes and suffixes added with CONCAT() part of the actual data in the tables, but this is still going to be a fairly inefficient query if you have a lot of data to search, because it cannot make use of indexes. If you need to do this kind of substring searching on large datasets efficiently, you may want to use something better suited for than specific purpose than MySQL.
You only have three columns in the table, so you are looking for combinations of 1, 2, and 3 elements.
For simplicity, I'll start with the following table:
select index, n1 as n from t union all
select index, n2 from t union all
select index, n3 from t union all
select distinct index, -1 from t union all
select distinct index, -2 from t
Let's call this "values". Now, we want to get all triples from this table for a given index. In this case, -1 and -2 represent NULL.
select (case when v1.n < 0 then NULL else v1.n end) as n1,
(case when v2.n < 0 then NULL else v2.n end) as n2,
(case when v3.n < 0 then NULL else v3.n end) as n3,
count(*) as NumOccurrences
from values v1 join
values v2
on v1.n < v2.n and v1.index = v2.index join
values v3
on v2.n < v3.n and v2.index = v3.index
This is using the join mechanism to generate the combinations.
This method finds all combinations regardless of ordering (so 1, 2, 3 is the same as 2, 3, 1). Also, this ignores duplicates, so it cannot find (1, 2, 2) if 2 is repeated twice.
SELECT
CONCAT(CAST(n1 AS VARCHAR(10)),'|',CAST(n2 AS VARCHAR(10)),'|',CAST(n3 AS VARCHAR(10))) AS Combination,
COUNT(CONCAT(CAST(n1 AS VARCHAR(10)),'|',CAST(n2 AS VARCHAR(10)),'|',CAST(n3 AS VARCHAR(10)))) AS Occurrences
FROM
MyTable
GROUP BY
CONCAT(CAST(n1 AS VARCHAR(10)),'|',CAST(n2 AS VARCHAR(10)),'|',CAST(n3 AS VARCHAR(10)))
This creates a single column that represents the combination of the values within the 3 columns by concatenating the values. It will count the occurrences of each.