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

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).

Related

How to treat missing id 's value as 0 and order by it?

When I use in keyword in sql, there may be some id is missing , but I want treat them like they exist and other columns are null or 0.
For example, suppose I have a table with two columns and some rows:
[id,value1]
1      1
2      4
3      3
5      5
I may write sql like this:
select * from table where id in (1,4,5) order by value1 limit 0,2 ;
When this sql is executed, the return result is [(1,1),(5,5)].
But what I want is [(4,0),(1,1)], because I want to treat the missing id 4 like it exists in the table.
So the question is : Is there some elegant way to achieve it using sql instead of select all rows and sort them in memory.
Use a left join:
select *
from (select 1 as id union all
select 4 union all
select 5
) i left join
table t
using (id)
order by t.value1
limit 0, 2 ;
Note that you are ordering by a value in the existing table, so this depends on the fact that NULL is ordered before other values.

SQL select single row with two matching values

I'm probably having a bad day, but this is somehow escaping me:
I want to return the second row in this table only.
userId val1 val2
1 11 12
2 13 14
3 13 15
4 16 17
Using SELECT * FROM table WHERE val1=13 AND val2=14 obviously returns 2 rows, the second and third. Whats the correct way to select ONLY the second row? Where val1 is 13 and val2 is 14?
EDIT: I'm an idiot.
Just use SELECT * FROM table WHERE val1=13 AND val2=14like you already mentioned in your question, because in fact, it actually returns only row number 2.
If it has been a very bad day & there is a typo in your question & val2 in third row also equals 14 - the only way your query would return two rows, then this would do what you want
SELECT *
FROM table
WHERE val1=13 AND val2=14
ORDER BY userId
LIMIT 1;
If you get 2 rows, then there must be 2 rows match the condition.
Maybe you could try:
select count(*)
from table
where val1=13 AND val2=14;
to show the size of result set.

Comparing attributes from the same table using SQL query

I have a contents table and the entires in it are as shown in the attached figure
There are more than 100,000 entries. I want to fetch the data where the update_date for commit=0 is greater than update_date for commit=1. I also need the corresponding row for commit=1.
I tried a few things, but takes a long time to retrieve the results. What is the best SQL query I can use. I am using MySQL database.
EDIT
I have now updated the table. There is an attribute called content_id which binds the rows together.
A query like this gives me half of what I want
select a.* from contents a, contents b where
a.content_id=b.content_id and
a.update_date > b.update_date and
a.committed=0 and b.committed=1
I also want the corresponding entries from committed=1, but they should be appended at the bottom as rows and not vertically concatenated as columns.
For example, I cannot use
select * from contents a, contents b where
a.content_id=b.content_id and
a.update_date > b.update_date and
a.committed=0 and b.committed=1
because the results from 'b' are appended vertically. Also, is there a better way to write this query. This works really slow if there are many entries in the database.
I am assuming that in the above example, you only need id=2 as for content id = 1, the update_date for commit=0 is greater than update_date for commit=1 and in that case you need data for commited = 1.
I an using Oracle, so you need to find a suitable replacement for row_number() funtion in mysql.
The logic would be
Create a view on the existing table to use rownumber so it will give rownumber like below order by time desc (see if you use a nested query to do it)
ID, CONTENT_ID, COMMITED, UPDATE_DATE, ROWN
2 1 1 06-SEP-15 00:00:56 1
1 1 0 07-SEP-15 00:00:56 2
3 2 0 03-SEP-15 00:00:56 1
4 2 1 04-SEP-15 00:00:56 2
Now select only rows where where rown=1 and commited=1
This is the query in oracle. The second with query c2 will be your view.
Oracle query
with c1 (id, content_id,commited,update_date) as
(
select 1,1,0,sysdate from dual union
select 2,1,1,sysdate-1 from dual union
select 3,2,0,sysdate-4 from dual union
select 4,2,1,sysdate-3 from dual
),
c2 as
(select c1.*,row_number() over(partition by content_id order by update_date) as rown from c1)
select id,content_id,commited,update_date from c2
where rown=1 and commited=1
ID, CONTENT_ID, COMMITED, UPDATE_DATE, ROWN
Output
ID, CONTENT_ID, COMMITED, UPDATE_DATE
2 1 1 06-SEP-15 00:06:17

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

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

Random row from big query result

I need to get 1-2 rows from query result retrived with SQL select on indexed columns without getting the whole record set.
For example I will retrieve 10 000 records using query
SELECT * FROM table WHERE field 1>1 AND field1 < 10
but I need only 1 random row from this query regarding to highload of my database.
I can use
SELECT * FROM table WHERE field 1>1 AND field1 < 10 LIMIT 100, 1
But I don't know records numebr to use correct offset range
How can I achieve this goal?
You could use ORDER BY RAND()
SELECT * FROM table WHERE field1 > 1 AND field1 < 10 ORDER BY RAND() LIMIT 1
This will return 1 random row with field1 in between 1 and 10
How about restricting the records you select in the first place?
SELECT * FROM table WHERE field1 IN (CONVERT(RAND()*10,SIGNED),CONVERT(RAND()*10,SIGNED)) LIMIT 2