How to take the near number in a column at Oracle Select - sql

I have an adress table and I need to take the near number. For example if I´m entenring the number 256 in this case I´ll take 257 because:
254<--256->257
Somebody knows the solution.
Thanks and sorry for my bad English.

The distance between 257 and the number is abs(number-257). So you can find (one of) the nearest numbers with:
select number
from (
select number
from yourtable
order by abs(number-257)
) sub
where rownum < 2

SELECT *
FROM mytable
WHERE mynumber BETWEEEN 256 - 2 AND 256 + 2
If you just need to pick the first match, use this:
SELECT *
FROM (
SELECT *
FROM (
SELECT *
FROM (
SELECT *
FROM mytable
WHERE mynumber <= 256
ORDER BY
mynumber DESC
)
WHERE rownum = 1
UNION ALL
SELECT *
FROM (
SELECT *
FROM mytable
WHERE mynumber > 256
ORDER BY
mynumber
)
WHERE rownum = 1
)
ORDER BY
ABS(256 - number), mynumber DESC
)
WHERE rownum = 1
This is more index efficient, since the final ORDER BY will sort at most two records.

I tried the sql in other way but based on the Quassnoi´s SQL. I´ve made some revisions and it´s working well. Look:
(SELECT * FROM
(SELECT number
FROM numberList
WHERE number < ?
ORDER BY number DESC)
WHERE ROWNUM=1)
UNION ALL
(SELECT * FROM
(SELECT number
FROM numberList
WHERE numeber > ?
ORDER BY number ASC)
WHERE ROWNUM=1)

Try this
Num integer := 236;
select Max(number) AS NearestAndGreatest from mytbl t
join(
select MIN(T.distance) AS dist from(
select number,abs(number-Num) AS distance from mytbl)T)X
on abs(number-Num) = X.dist

Related

Randomly flagging records in an Oracle Table

Given a table of IDs in an Oracle database, what is the best method to randomly flag (x) percent of them? In the example below, I am randomly flagging 20% of all records.
ID ELIG
1 0
2 0
3 0
4 1
5 0
My current approach shown below works fine, but I am wondering if there is a more efficient way to do this?
WITH DAT
AS ( SELECT LEVEL AS "ID"
FROM DUAL
CONNECT BY LEVEL <= 5),
TICKETS
AS (SELECT "ID", 1 AS "ELIG"
FROM ( SELECT *
FROM DAT
ORDER BY DBMS_RANDOM.VALUE ())
WHERE ROWNUM <= (SELECT ROUND (COUNT (*) / 5, 0) FROM DAT)),
RAFFLE
AS (SELECT "ID", 0 AS "ELIG"
FROM DAT
WHERE "ID" NOT IN (SELECT "ID"
FROM TICKETS)
UNION
SELECT * FROM TICKETS)
SELECT *
FROM RAFFLE;
You could use a ROW_NUMBER approach here:
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY dbms_random.value) rn,
COUNT(*) OVER () cnt
FROM yourTable t
)
SELECT t.*,
CASE WHEN rn / cnt <= 0.2 THEN 'FLAG' END AS flag -- 0.2 to flag 20%
FROM cte t
ORDER BY ID;
Demo
Sample output for one run of the above query:
Note that one of five records is flagged, which is 20%.

Applying LIMIT and OFFSET to MS SQL server 2008 queries

I need to apply LIMIT and OFFSET to original query (without modifying it) in MSSQL server 2008.
Let's say the original query is:
SELECT * FROM energy_usage
(But it can be any arbitrary SELECT query)
That's what I came up with so far:
1. It does what I need, but the query generates extra column row_number which I don't need.
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM energy_usage
)
SELECT * FROM OrderedTable WHERE row_number BETWEEN 1 AND 10
2. This one doesn't work for some reason and returns the following error.
SELECT real_sql.* FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM (SELECT * FROM energy_usage) as real_sql) as subquery
WHERE row_number BETWEEN 1 AND 10
More common case is:
SELECT real_sql.* FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM (real sql query) as real_sql) as subquery
WHERE row_number BETWEEN {offset} + 1 AND {limit} + {offset}
Error:
The column prefix 'real_sql' does not match with a table name or alias
name used in the query.
Simply do not put it on SELECT list:
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, *
FROM energy_usage
)
SELECT col1, col2, col3 FROM OrderedTable WHERE row_number BETWEEN 1 AND 10;
SELECT * is common anti-pattern and should be avoided anyway. Plus ORDER BY (SELECT 1) will not give you guarantee of stable sort between executions.
Second if you need only ten rows use:
SELECT TOP 10 *
FROM energy_usage
ORDER BY ...
Unfortunately you won't get something nice as Selecting all Columns Except One
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, *
FROM energy_usage
)
SELECT * EXCEPT row_number FROM OrderedTable WHERE row_number BETWEEN 1 AND 10;
This would solve the problem.
DECLARE #offset INT = 1;
DECLARE #limit INT = 10;
WITH Filtered AS (
SELECT TOP (#offset + #limit) *
FROM energy_usage
ORDER BY 1 ASC
), Results AS (
SELECT TOP (#limit) *
FROM Filtered
ORDER BY 1 DESC
)
SELECT *
FROM Results
ORDER BY 1 ASC;

Can you select the Nth row in the dataset returned by SELECT statement?

In pseudo-code, what I mean is, can you do something like this (similar to TOP):
SELECT Row 2
*
FROM Table
WHERE Column1 = Condition
I wanted to do something for testing - I can't think of a real world scenario for this. Just curious if it's possible. I can't find anything on the Interwebz about it.
This would give you the 9th row:
select top 1 *
from (
select top 9 *
from MyTable
order by 1) as x
order by 1 desc
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY col2) rn
FROM mytable
WHERE col1 = condition
) q
WHERE rn = 4
WITH QUERY AS (
SELECT *, ROW_NUMBER() OVER(ORDER BY Column1) AS RowNumber
FROM Table
)
SELECT * FROM QUERY WHERE ROWNUMBER = N

Sql Select top 2 , bottom 2 and 6 random records

How to select top 2 , bottom 2 and 6 random (not in Top 2 and Bottom 2) records of the table using one SQL select query?
In MS SQL 2005/2008:
with cte
as
(
select
row_number() over (order by name) RowNumber,
row_number() over (order by newid()) RandomOrder,
count(*) over() Total,
*
from sys.tables
)
select *
from cte
where RowNumber <= 2 or Total - RowNumber + 1 <= 2
union all
select *
from
(
select top 6 *
from cte
where RowNumber > 2 and Total - RowNumber > 2
order by RandomOrder
) tt
Replace sys.tables with your table name and alter order by name to specify order condition for top 2 and bottom 2.
Perhaps not a single select statment, but it can be executed in one call:
/* Top 2 - change order by to get the 'proper' top 2 */
SELECT * from table ORDER BY id DESC LIMIT 2
UNION ALL
/* Random 6.. You may want to add a WHERE and random data to get the random 6 */
/* Old Statement before edit - SELECT * from table LIMIT 6 */
SELECT * from table t
LEFT JOIN (SELECT * from table ORDER BY id DESC LIMIT 2) AS top ON top.id = t.id
LEFT JOIN (SELECT * from table ORDER BY id DESC LIMIT 2) AS bottom ON bottom.id = t.id
WHERE ISNULL(top.id ) AND ISNULL(bottom.id)
ORDER BY RANDOM()
LIMIT 6
UNION ALL
/* Bottom 2 - change order by to get the 'proper' bottom 2 */
SELECT * from table ORDER BY id ASC LIMIT 2
Something along those lines. Basically the UNION All is the trick.
Assuming the "order" is by the id column:
select * from (select id, id from my_table order by id limit 2) t1
union
select * from (select id, id from my_table where id not in (
select * from (select id from my_table order by id asc limit 2) t22
union
select * from (select id from my_table order by id desc limit 2 ) t23)
order by rand()
limit 6) t2
union
select * from (select id, id from my_table order by id desc limit 2) t3
EDIT: Fixed syntax and tested query - it works

How do I intermerge multiple SELECT results?

First of all, I'm not familiar with SQL in depth, so this may be a beginner question.
I know how to select data ordered by Id: SELECT * FROM foo ORDER BY id LIMIT 100 as well as how to select a random subset: SELECT * FROM foo ORDER BY RAND() LIMIT 100.
I'd like to merge these two queries into 1 in a zip manner, choosing limit/2 from each (i.e. 50). For example:
0
85
1
35
2
38
3
19
4
...
I would like to avoid duplicates. The easiest way is probably to just add a WHERE id > 100/2 to the part of the query that retrieves randomly ordered rows.
Additional info: It is unknown how many rows exist.
To get the "zip-manner" merge add a generated rownumber to each query and use an union with order by rownnumber.
Use even numbers for one and odd numbers for the other query.
Try this for MySQL
SELECT
#rownum0:=#rownum0+2 rn,
f.*
FROM ( SELECT * FROM foo ORDER BY id ) f, (SELECT #rownum0:=0) r
UNION
SELECT #rownum1:=#rownum1+2 rn,
b.*
FROM ( SELECT * FROM bar ORDER BY RAND() ) b, (SELECT #rownum1:=-1) r
ORDER BY rn
LIMIT 100
This should be self-explicative but doesnt remove duplicates:
select #rownum:=#rownum+1 as rownum,
(#rownum-1) % 50 as sortc, u.id
from (
(select id from player order by id limit 50)
union all
(select id from player order by rand() limit 50)) u,
(select #rownum:=0) r
order by sortc,rownum;
If you replace "union all" with "union", you remove duplicates but get less rows as a consequence.
This will deal with duplicates, does not restrict random numbers in the ids > 50, and always return 100 rows:
SELECT #rownum := #rownum + 1 AS rownum,
( #rownum - 1 ) % 50 AS sortc,
u.id
FROM ((SELECT id
FROM foo
ORDER BY Rand()
LIMIT 50)
UNION
(SELECT id
FROM foo
WHERE id <= 100
ORDER BY id)) u,
(SELECT #rownum := 0) r
WHERE #rownum < 100
ORDER BY sortc,
rownum DESC
LIMIT 100;
SELECT * FROM foo ORDER BY id LIMIT 50 UNION SELECT * FROM foo ORDER BY RAND() LIMIT 50
If I understand your requirement correctly. UNION removes duplicates by itself