I need to select some columns from a table and also some other columns depending on a value from one column. For example:
SELECT
col_a, col_b, col_c
FROM user_table
INNER JOIN col_d, col_e, col_f ON col_type = 1
INNER JOIN col_g, col_h, col_i ON col_type = 2
WHERE id = :id
AND active = 1
Since I'm new to mysql, I don't how how i should do this. I tried using CASE WHEN but was able to select a single column and I need to select multiple columns.
I think you misunterstood INNER JOIN and you want something like that
SELECT
col_a, col_b, col_c,
IF( col_type = 1, col_d, NULL ) AS col_d,
IF( col_type = 1, col_e, NULL ) AS col_e,
IF( col_type = 2, col_g, NULL ) AS col_g,
IF( col_type = 2, col_h, NULL ) AS col_h
FROM user_table
WHERE id = :id
AND active = 1
But i prefere to load all cols and handle it with your other language (PHP?).
Related
SELECT table_grouping_code, gui_field_code, gui_interface_id, dictionary_code, property_name, position INTO #test
FROM table_grouping_layout
WHERE company_code = "TEST";
SELECT table_grouping_code, gui_field_code, gui_interface_id, dictionary_code, property_name, position INTO #doximtrx
FROM table_grouping_layout
WHERE company_code = "DOXIMTRX";
select #test = #doximtrx;
I tried this, that seems very logica to me, but mysql says: ERROR CODE 1222: the used select statement have a different number of columns. How can it be possible?? I copied the query, only changing the code.
Example of data in the table
You are trying to select many columns into a single variable. The correct syntax for that part of your query is:
SELECT
GROUP_CONCAT(table_grouping_code ORDER BY table_grouping_code)
, GROUP_CONCAT(gui_field_code ORDER BY gui_field_code)
, GROUP_CONCAT(gui_interface_id ORDER BY gui_interface_id)
, GROUP_CONCAT(dictionary_code ORDER BY dictionary_code)
, GROUP_CONCAT(property_name ORDER BY property_name)
, GROUP_CONCAT(position ORDER BY position)
FROM
table_grouping_layout
WHERE
company_code = 'TEST'
INTO
#t1, #t2, #t3, #t4, #t5, #t6;
SELECT
GROUP_CONCAT(table_grouping_code ORDER BY table_grouping_code)
, GROUP_CONCAT(gui_field_code ORDER BY gui_field_code)
, GROUP_CONCAT(gui_interface_id ORDER BY gui_interface_id)
, GROUP_CONCAT(dictionary_code ORDER BY dictionary_code)
, GROUP_CONCAT(property_name ORDER BY property_name)
, GROUP_CONCAT(position ORDER BY position)
FROM
table_grouping_layout
WHERE
company_code = 'DOXIMTRX'
INTO
#d1, #d2, #d3, #d4, #d5, #d6;
How to compare results is a different story:
SELECT ifNull(
#t1 = #d1
and #t2 = #d2
and #t3 = #d3
and #t4 = #d4
and #t5 = #d5
and #t6 = #d6
, false
) as result;
TEST DATA:
select * from table_grouping_layout;
table_grouping_code
gui_field_code
gui_interface_id
dictionary_code
property_name
position
company_code
1
2
3
4
5
6
DOXIMTRX
1
3
3
4
5
6
TEST
1
1
3
4
5
6
DOXIMTRX
1
2
3
4
5
6
TEST
QUERY RESULT:
result
0
Variables are meant to hold one value. Neither one row nor one column nor one table.
Comparing two data sets can be achieved with a combination of UNIONand EXCEPT or with a full outer join. Unfortunately, MySQL does neither support EXCEPT nor FULL OUTER JOIN.
Here is a workaround:
with t as (select * from table_grouping_layout where company_code = 'TEST')
, d as (select * from table_grouping_layout where company_code = 'DOXIMTRX')
select 'MISMATCH' as status, t.position
from table_grouping_layout t
join table_grouping_layout d
on t.position = d.position
and not
(
t.table_grouping_code <=> t.table_grouping_code and
t.gui_field_code <=> t.gui_field_code and
t.gui_interface_id <=> t.gui_interface_id and
t.dictionary_code <=> t.dictionary_code and
t.property_name <=> t.property_name
)
union all
select 'TEST MISSING' as status, position
from d
where position not in (select position from t)
union all
select 'DOXIMTRX MISSING' as status, position
from t
where position not in (select position from d)
order by position;
Another approach using aggregation:
select
position,
case
when sum(company_code = 'TEST') = 0 then 'TEST MISSING'
when sum(company_code = 'DOXIMTRX') = 0 then 'DOXIMTRX MISSING'
when not
(
max(table_grouping_code) <=> min(table_grouping_code) and count(table_grouping_code) in (0,2)
max(gui_field_code) <=> min(gui_field_code) and count(gui_field_code) in (0,2)
max(gui_interface_id) <=> min(gui_interface_id) and count(gui_interface_id) in (0,2)
max(dictionary_code) <=> min(dictionary_code) and count(dictionary_code) in (0,2)
max(property_name) <=> min(property_name) and count(property_name) in (0,2)
) then 'MISMATCH'
else 'MATCH' end as status
from table_grouping_layout
where company_code in ('TEST', 'DOXIMTRX')
group by position
order by position;
Basically I need to merge these into one single query:
SELECT COUNT( DISTINCT id ) AS totalRows1
FROM other_events WHERE status = "approved"
AND Location = 1
SELECT COUNT( DISTINCT Id ) AS totalRows2
FROM core_events WHERE Status = "Active"
AND Location_id = 1
When I do it like below, if there is no event with Location_id = 1 query returns 0. In that condition I need it to return the count of the first table only.
SELECT COUNT( DISTINCT t1.id ) + COUNT( DISTINCT t2.Id ) AS total
FROM other_events AS t1, core_events AS t2
WHERE t1.status = "approved"
AND t1.Location = 1
AND t2.Location_id = 1
AND t2.Status = 'Active'
ps. column names are exactly like above
Use a UNION statement to merge the result like this:
SELECT SUM(total) FROM (
SELECT COUNT(DISTINCT id) AS total
FROM other_events
WHERE (status = "approved" AND Location = 1)
UNION ALL
SELECT COUNT(DISTINCT Id) AS total
FROM core_events
WHERE (Location_id = 1 AND Status = 'Active')
) union_result
Data
name 'chan' value 'a'
name 'chan' value 'b'
name 'max' value 'a'
name 'max' value 'b'
name 'tony' value 'a'
name 'tony' value 'c'
I need to find out user who both have value a and b, this is my solution:
SELECT * FROM `table`
GROUP BY `name`
HAVING SUM(IF(`value` = 'a', 1, 0)) >= 1 AND SUM(IF(`value` = 'b', 1, 0)) >= 1
Any better way?
Your solution is ok, but it would be better written as:
SELECT name
FROM `table`
GROUP BY `name`
HAVING SUM(`value` = 'a') >= 1 AND SUM(`value` = 'b') >= 1;
A possibly more efficient form is:
SELECT name
FROM `table`
WHERE value in ('a', 'b')
GROUP BY `name`
HAVING COUNT(DISTINCT value) = 2;
And, depending on your data structure and indexes and size, this could also be efficient:
select ta.name
from table ta join
table tb
on ta.name = tb.name and ta.value = 'a' and tb.value = 'b';
I prefer the methods using group by and having because they generalize to a more diverse set of conditions.
Try this out. a DISTINCT is required if name per value in not unique.
eg (COUNT(DISTINCT value) = 2)
SELECT name
FROM tableName
WHERE value IN ('a', 'b')
GROUP BY name
HAVING COUNT(*) = 2
I have a table in a MySQL DB with an UNIQUE INT(10) column. The table is pretty populated and the row contains non-consecutive entries of Integer numbers in that column. I would like to do a query, which gets me the smallest number (or the n smallest numbers) that is not in any row.
Example: The table contains rows with values (1, 2, 3, 5, 7, 8, 10, 12, 15) for the column. The sql statement should return i.e. the five lowest non-contained values, which are 4, 6, 9, 11, 13 in this case.
Is this possible with MySQL?
You can use a "numbers" table (it's handy for various operations):
CREATE TABLE num
( i UNSIGNED INT NOT NULL
, PRIMARY KEY (i)
) ;
INSERT INTO num (i)
VALUES
(1), (2), ..., (1000000) ;
Then:
SELECT
num.i
FROM
num
LEFT JOIN
tableX AS t
ON num.i = t.columnX
WHERE
t.columnX IS NULL
ORDER BY
num.i
LIMIT 5
or:
SELECT
num.i
FROM
num
WHERE
NOT EXISTS
( SELECT *
FROM tableX AS t
WHERE num.i = t.columnX
)
ORDER BY
num.i
LIMIT 5
Another approach, without using an auxilary table, would be to use MySQL variables. You can test it in SQL-Fiddle, test-2. The output is not the same as the previous (just to show that it can be done):
SELECT start_id, end_id
FROM
( SELECT
IF( t.columnX <> #id, #id, NULL) AS start_id
, IF( t.columnX <> #id, t.columnX-1, NULL) AS end_id
, #rows := #rows + (t.columnX - #id) AS r
, #id := t.columnX + 1 AS running_id
FROM
tableX AS t
CROSS JOIN
( SELECT #rows := 0
, #id := 1
) AS dummy
WHERE
#rows < 5
ORDER BY
t.columnX
) AS tmp
WHERE
start_id IS NOT NULL
This will work, but I think it is pretty inefficient. You won't need an extra table though (a table that would be (2^31-1)*4/1024^3 = 8GB for all positive numbers in INT). Also I advise you look at why you need this, because it might not be neccesary.
Also it will return the start and end of a range, but not all numbers in that range. (e.g. if you have numbers 1 and 5 it will return {0,2,4,6})
SELECT (t.num-1) AS bound FROM t
WHERE t.num-1 NOT IN (SELECT t.num FROM t)
UNION
SELECT (t.num+1) AS bound FROM t
WHERE t.num+1 NOT IN (SELECT t.num FROM t)
As I said this will be pretty inefficient, JOINs might be faster but you would need benchmark it.
SELECT (t.num-1) AS bound FROM t
LEFT JOIN t AS u ON t.num-1 = u.num
WHERE u.num IS NULL
UNION
SELECT (t.num+1) AS bound FROM t
LEFT JOIN t AS u ON t.num+1 = u.num
WHERE u.num IS NULL
mytable
pid name field
=== ==== =====
1 A1 0
2 A2 1
3 A3 1
4 A4 0
5 A5 0
This is my table structure. Here I want to select randomly 4 rows so I use RAND() mysql function in my query
my questions is
How do I pair to rows. I mean, I wanna select pid 2 and 3 always one ofter another.
I need in bellow order. i don't want to break the pair A2 A3
A1 A2 A3 A4 or A2 A3 A4 A1 or A2 A3 A4 A5 or A4 A5 A2 A3 and etc
I used the query below but it's not working for me
SELECT * FROM mytable ORDER BY RAND() ASC limit 0,4
turbod was close with his answer, he was just ordering randomly, when it seems you wanted to order by pid, after getting the random rows you wanted in conjunction with the ones concerning A2 and A3:
(
SELECT *
FROM `mytable`
WHERE
name ='A2' OR
name ='A3'
LIMIT 2
)
UNION
(
SELECT DISTINCT *
FROM `mytable`
WHERE
name !='A2' OR
name !='A3'
ORDER BY RAND( ) LIMIT 2
)
ORDER BY `pid`
Generally, using ORDER BY RAND() is not a good idea. Please read the text by Jan Kneschke, showing why: http://jan.kneschke.de/projects/mysql/order-by-rand/
I ran a heavy test on this, passed.
(
SELECT * , 0.5 AS ordercol
FROM `mytable`
WHERE `name`IN ( "A2", "A3" )
LIMIT 2
)
UNION (
SELECT * , rand() AS ordercol
FROM `mytable`
WHERE `name` NOT IN ( "A2", "A3" )
LIMIT 2
)
ORDER BY ordercol, `name` IN ( "A2", "A3" ) , `name` ="A3"
This will do the job very well. But to make the result even more random, execute that statement with replacing that 0.5 value in 1st line with a random value chosen by your client application code like mt_rand(0, 1000000) / 1000000 in PHP . Make sure it falls between 0 and 1. But do NOT use mysql function rand() in place of that 0.5 because it will make A2 and A3 apart from each other. The trick is assigning a random value for "ordercol" in all rows but keep it same for A2 and A3
EDIT:
I believe we can replace the 0.5 value with a LEFT JOIN even instead of relying on discrete value by PHP, as we replace the first segment of the union, so the whole query becomes:
(
SELECT mt1.* , mt2.ordercol AS ordercol
FROM `mytable` AS mt1
LEFT JOIN (
SELECT RAND( ) AS ordercol
) AS mt2 ON TRUE
WHERE `name`
IN (
"A2", "A3"
)
LIMIT 2
)
UNION (
SELECT * , rand() AS ordercol
FROM `mytable`
WHERE `name` NOT IN ( "A2", "A3" )
LIMIT 2
)
ORDER BY ordercol, `name` IN ( "A2", "A3" ) , `name` ="A3"
I doubt there is a sane way to this in MySQL only.
I can think of one way of doing it, assuming you are using PHP/MySQL:
Essentially you query everything but A3, then put A3 next to A2
$res = mysql_query("SELECT name, field FROM mytable WHERE name <> 'A3' ORDER BY RAND()");
$res2 = mysql_query("SELECT name, field FROM mytable WHERE name = 'A3'");
$data = array();
while($row = mysql_fetch_array($res))
{
array_push($data, $row);
if ($row['name'] == "A2")
{
$row2 = mysql_fetch_array($res2);
array_push($data, $row2);
}
}
Now $data will contain your results in the desired order.
If you are always selecting all the rows in the table:
SELECT pid, name, field, idx
FROM (
SELECT pid, name, field,
#pos := IF(name = 'A3', #idx, #pos),
#idx := #idx + IF(name = 'A3', 2, 1), idx
FROM mytable, (SELECT #pos = -1, #idx := 0) dm
WHERE name <> 'A2'
ORDER BY RAND()
)
UNION SELECT pid, name, field, #pos + 1 idx
FROM mytable
WHERE name = 'A2'
ORDER BY idx;
If you are not always returning all the rows, thus need to check if A3 was returned to know if A2 should be included:
SELECT pid, name, field, idx
FROM (
SELECT pid, name, field,
#pos := IF(name = 'A3', #idx, #pos),
#idx := #idx + IF(name = 'A3', 2, 1), idx
FROM mytable, (SELECT #pos = -1, #idx := 0) dm
WHERE name <> 'A2'
ORDER BY RAND()
LIMIT 4
)
UNION SELECT pid, name, field, #pos + 1 idx
FROM mytable
WHERE #pos != -1 AND name = 'A2'
ORDER BY idx;
SELECT * FROM (
SELECT DISTINCT name FROM mytable
WHERE name <> 'A2' AND name <> 'A3'
ORDER BY RAND()
LIMIT 0,2
UNION
SELECT DISTINCT name FROM mytable
WHERE name = 'A2' OR name = 'A3'
ORDER BY name
)whateverQueryAlias
ORDER BY RAND()
That should do it.
Here is my solution:
SELECT *
FROM `mytable`
WHERE name ='A2'
OR name ='A3'
LIMIT 2
UNION
(SELECT DISTINCT *
FROM `mytable`
WHERE name !='A2'
OR name !='A3'
ORDER BY RAND( ) LIMIT 2) ORDER BY RAND()
SELECT *, RAND() "xrand" FROM yourtable A ORDER BY xrand LIMIT 4
SELECT * FROM `mytable` order by rand(), name asc limit 4.
i think this will satisfy your need.