Select randomly And Sort the records - sql

I have a table which has ID,name and Level columns.I want to SELECT the records of the Table by this pattern :
First Select Them randomly and then sort those random records by level column.
for example : my sample table and records:
ID name Level
---------------------------------
1 red-book 1
2 blue-pen 10
3 black-board 12
4 balck-Book 1
5 white-book 1
6 red-pen 10
7 green-pen 10
And the result should be something like this :
ID name level
------------------------------------------
3 black-board 12
6 red-pen 10
2 blue-pen 10
7 green-pen 10
4 balck-Book 1
1 red-book 1
5 white-book 1
I've also used
SELECT * FROM MyTable ORDER BY NEWID(),Level DESC
And
SELECT * FROM
(SELECT * FROM MyTable ORDERBY NEWID())As TempTbl
ORDER BY Level DESC
And
CREATE TABLE #MyTempTable (ID INT,name Nvarchar(256),Levels INT)
INSERT INTO #MyTempTable SELECT * FROM MyTable ORDER BY NEWID()
SELECT * FROM #MyTempTable ORDER BY Levels DESC

SELECT ID,name,level
FROM sample
ORDER BY level DESC,NEWID()

Related

How to get closest n rows for specific row in table?

I have a table foo with its primary key id and some other columns.
My goal is to find for instance rows with id=3 and id=4 and rows with id=6 and id=7 for row with id=5 - in case I would like to find 2 closest previous and next rows.
In case there is only one or no such rows (e.g. for id=2 there is only previous row) I would like to get only possible ones.
The problem is there can be some rows missing.
Is there a common practice to make such queries?
I would try the following:
SELECT * FROM table WHERE id > ? ORDER BY id ASC LIMIT 2
followed by
SELECT * FROM table WHERE id <= ? ORDER BY id DESC LIMIT 2
You may be able to combine the above into the following:
SELECT * FROM table WHERE id > ? ORDER BY id ASC LIMIT 2
UNION
SELECT * FROM table WHERE id <= ? ORDER BY id DESC LIMIT 2
I think this would fit your description.
Select * from table where id between #n-2 and #n+2 and id <> #n
One way is this:
with your_table(id) as(
select 1 union all
select 2 union all
select 4 union all
select 5 union all
select 10 union all
select 11 union all
select 12 union all
select 13 union all
select 14
)
select * from (
(select * from your_table where id <= 10 order by id desc limit 3+1)
union all
(select * from your_table where id > 10 order by id limit 3)
) t
order by id
(Here 10 is start point and 3 is n rows you want)
This is a possible solution by numbering all the records and fetching those where row number is 2 rows greater or lower than the selected ID.
create table foo(id int);
insert into foo values (1),(2),(4),(6),(7),(8),(11),(12);
-- using ID = 6
with rnum as
(
select id, row_number() over (order by id) rn
from foo
)
select *
from rnum
where rn >= (select rn from rnum where id = 6) - 2
and rn <= (select rn from rnum where id = 6) + 2;
id | rn
-: | -:
2 | 2
4 | 3
6 | 4
7 | 5
8 | 6
-- using ID = 2
with rnum as
(
select id, row_number() over (order by id) rn
from foo
)
select *
from rnum
where rn >= (select rn from rnum where id = 2) - 2
and rn <= (select rn from rnum where id = 2) + 2;
id | rn
-: | -:
1 | 1
2 | 2
4 | 3
6 | 4
dbfiddle here

oracle sql - N number of rows for for distinct value i

Suppose I have TableA w/ ID column:
TableA
ID
1
2
3
I'm hoping to get N rows returned for each distinct id in TableA
(example below is for N=3)
EXPECTED OUTPUT
ID SEQ
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
Is this possible w/ a single SQL statement?
Thanks!!
To get only rows with single ID
SELECT * FROM tab_name WHERE col1 = N ORDER BY col2 [DESC]
To Get how many records under each id
SELECT id, count(*) as count FROM tab_name GROUP BY id
To get specific number of rows per specific ID
SELECT *
FROM
(SELECT id, col2, ROW_NUMBER() OVER(PARTITION BY t.id ORDER BY col3) colX
FROM tab_name t) outerT
WHERE
outerT.colX < N + 1

row with minimum value of a column

Having this selection:
id IDSLOT N_UM
------------------------
1 1 6
2 6 2
3 2 1
4 4 1
5 5 1
6 8 1
7 3 1
8 7 1
9 9 1
10 10 0
I would like to get the row (only one) which has the minimun value of N_UM, in this case the row with id=10 (10 0).
select * from TABLE_NAME order by COLUMN_NAME limit 1
I'd try this:
SELECT TOP 1 *
FROM TABLE1
ORDER BY N_UM
(using SQL Server)
Try this -
select top 1 * from table where N_UM = (select min(N_UM) from table);
Use this sql query:
select id,IDSLOT,N_UM from table where N_UM = (select min(N_UM) from table));
Method 1:
SELECT top 1 *
FROM table
WHERE N_UM = (SELECT min(N_UM) FROM table);
Method 2:
SELECT *
FROM table
ORDER BY N_UM
LIMIT 1
A more general solution to this class of problem is as follows.
Method 3:
SELECT *
FROM table
WHERE N_UM IN (SELECT MIN(N_UM) FROM table);
Here is one approach
Create table #t (
id int,
IDSLOT int,
N_UM int
)
insert into #t ( id, idslot, n_um )
VALUES (1, 1, 6),
(2,6,2),
(3,2,1),
(4,4,1),
(5,5,1),
(6,8,1),
(7,3,1),
(8,7,1),
(9,9,1),
(10, 10, 0)
select Top 1 *
from #t
Where N_UM = ( select MIN(n_um) from #t )
select TOP 1 Col , COUNT(Col) as minCol from employee GROUP by Col
order by mindep asc

TSQL Sweepstakes Script

I need to run a sweepstakes script to get X amount of winners from a customers table. Each customer has N participations. The table looks like this
CUSTOMER-A 5
CUSTOMER-B 8
CUSTOMER-C 1
I can always script to have CUSTOMER-A,B and C inserted 5, 8 and 1 times respectively in a temp table and then select randomly using order by newid() but would like to know if there's a more elegant way to address this.
(Update: Added final query.)
(Update2: Added single query to avoid temp table.)
Here's the hard part using a recursive CTE plus the final query that shows "place".
Code
DECLARE #cust TABLE (
CustomerID int IDENTITY,
ParticipationCt int
)
DECLARE #list TABLE (
CustomerID int,
RowNumber int
)
INSERT INTO #cust (ParticipationCt) VALUES (5)
INSERT INTO #cust (ParticipationCt) VALUES (8)
INSERT INTO #cust (ParticipationCt) VALUES (1)
INSERT INTO #cust (ParticipationCt) VALUES (3)
INSERT INTO #cust (ParticipationCt) VALUES (4)
SELECT * FROM #cust
;WITH t AS (
SELECT
lvl = 1,
CustomerID,
ParticipationCt
FROM #Cust
UNION ALL
SELECT
lvl = lvl + 1,
CustomerID,
ParticipationCt
FROM t
WHERE lvl < ParticipationCt
)
INSERT INTO #list (CustomerID, RowNumber)
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY NEWID())
FROM t
--<< All rows
SELECT * FROM #list ORDER BY RowNumber
--<< All customers by "place"
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY MIN(RowNumber)) AS Place
FROM #list
GROUP BY CustomerID
Results
CustomerID ParticipationCt
----------- ---------------
1 5
2 8
3 1
4 3
5 4
CustomerID RowNumber
----------- -----------
4 1
1 2
1 3
2 4
1 5
5 6
2 7
2 8
4 9
2 10
2 11
2 12
1 13
5 14
5 15
3 16
5 17
1 18
2 19
2 20
4 21
CustomerID Place
----------- -----
4 1
1 2
2 3
5 4
3 5
Single Query with No Temp Table
It is possible to get the answer with a single query that does not use a temp table. This works fine, but I personally like the temp table version better so you can validate the interim results.
Code (Single Query)
;WITH List AS (
SELECT
lvl = 1,
CustomerID,
ParticipationCt
FROM #Cust
UNION ALL
SELECT
lvl = lvl + 1,
CustomerID,
ParticipationCt
FROM List
WHERE lvl < ParticipationCt
),
RandomOrder AS (
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY NEWID()) AS RowNumber
FROM List
)
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY MIN(RowNumber)) AS Place
FROM RandomOrder
GROUP BY CustomerID
try this:
Select Top X CustomerId
From (Select CustomerId,
Rand(CustomerId) *
Count(*) /
(Select Count(*)
From Table) Sort
From Table
Group By CustomerId) Z
Order By Sort Desc
EDIT: abovbe assumed multiple rows per customer, one row per participation... Sorry, following assumes one row per customer, with column Participations holding number of participations for that customer.
Select Top 23 CustomerId
From ( Select CustomerId,
Participations - RAND(CustomerId) *
(Select SUM(Participations ) From customers) sort
from customers) Z
Order By sort desc

What is the SQL for 'next' and 'previous' in a table?

I have a table of items, each of which has a date associated with it. If I have the date associated with one item, how do I query the database with SQL to get the 'previous' and 'subsequent' items in the table?
It is not possible to simply add (or subtract) a value, as the dates do not have a regular gap between them.
One possible application would be 'previous/next' links in a photo album or blog web application, where the underlying data is in a SQL table.
I think there are two possible cases:
Firstly where each date is unique:
Sample data:
1,3,8,19,67,45
What query (or queries) would give 3 and 19 when supplied 8 as the parameter? (or the rows 3,8,19). Note that there are not always three rows to be returned - at the ends of the sequence one would be missing.
Secondly, if there is a separate unique key to order the elements by, what is the query to return the set 'surrounding' a date? The order expected is by date then key.
Sample data:
(key:date) 1:1,2:3,3:8,4:8,5:19,10:19,11:67,15:45,16:8
What query for '8' returns the set:
2:3,3:8,4:8,16:8,5:19
or what query generates the table:
key date prev-key next-key
1 1 null 2
2 3 1 3
3 8 2 4
4 8 3 16
5 19 16 10
10 19 5 11
11 67 10 15
15 45 11 null
16 8 4 5
The table order is not important - just the next-key and prev-key fields.
Both TheSoftwareJedi and Cade Roux have solutions that work for the data sets I posted last night. For the second question, both seem to fail for this dataset:
(key:date) 1:1,2:3,3:8,4:8,5:19,10:19,11:67,15:45,16:8
The order expected is by date then key, so one expected result might be:
2:3,3:8,4:8,16:8,5:19
and another:
key date prev-key next-key
1 1 null 2
2 3 1 3
3 8 2 4
4 8 3 16
5 19 16 10
10 19 5 11
11 67 10 15
15 45 11 null
16 8 4 5
The table order is not important - just the next-key and prev-key fields.
Select max(element) From Data Where Element < 8
Union
Select min(element) From Data Where Element > 8
But generally it is more usefull to think of sql for set oriented operations rather than iterative operation.
Self-joins.
For the table:
/*
CREATE TABLE [dbo].[stackoverflow_203302](
[val] [int] NOT NULL
) ON [PRIMARY]
*/
With parameter #val
SELECT cur.val, MAX(prv.val) AS prv_val, MIN(nxt.val) AS nxt_val
FROM stackoverflow_203302 AS cur
LEFT JOIN stackoverflow_203302 AS prv
ON cur.val > prv.val
LEFT JOIN stackoverflow_203302 AS nxt
ON cur.val < nxt.val
WHERE cur.val = #val
GROUP BY cur.val
You could make this a stored procedure with output parameters or just join this as a correlated subquery to the data you are pulling.
Without the parameter, for your data the result would be:
val prv_val nxt_val
----------- ----------- -----------
1 NULL 3
3 1 8
8 3 19
19 8 45
45 19 67
67 45 NULL
For the modified example, you use this as a correlated subquery:
/*
CREATE TABLE [dbo].[stackoverflow_203302](
[ky] [int] NOT NULL,
[val] [int] NOT NULL,
CONSTRAINT [PK_stackoverflow_203302] PRIMARY KEY CLUSTERED (
[ky] ASC
)
)
*/
SELECT cur.ky AS cur_ky
,cur.val AS cur_val
,prv.ky AS prv_ky
,prv.val AS prv_val
,nxt.ky AS nxt_ky
,nxt.val as nxt_val
FROM (
SELECT cur.ky, MAX(prv.ky) AS prv_ky, MIN(nxt.ky) AS nxt_ky
FROM stackoverflow_203302 AS cur
LEFT JOIN stackoverflow_203302 AS prv
ON cur.ky > prv.ky
LEFT JOIN stackoverflow_203302 AS nxt
ON cur.ky < nxt.ky
GROUP BY cur.ky
) AS ordering
INNER JOIN stackoverflow_203302 as cur
ON cur.ky = ordering.ky
LEFT JOIN stackoverflow_203302 as prv
ON prv.ky = ordering.prv_ky
LEFT JOIN stackoverflow_203302 as nxt
ON nxt.ky = ordering.nxt_ky
With the output as expected:
cur_ky cur_val prv_ky prv_val nxt_ky nxt_val
----------- ----------- ----------- ----------- ----------- -----------
1 1 NULL NULL 2 3
2 3 1 1 3 8
3 8 2 3 4 19
4 19 3 8 5 67
5 67 4 19 6 45
6 45 5 67 NULL NULL
In SQL Server, I prefer to make the subquery a Common table Expression. This makes the code seem more linear, less nested and easier to follow if there are a lot of nestings (also, less repetition is required on some re-joins).
Firstly, this should work (the ORDER BY is important):
select min(a)
from theTable
where a > 8
select max(a)
from theTable
where a < 8
For the second question that I begged you to ask...:
select *
from theTable
where date = 8
union all
select *
from theTable
where key = (select min(key)
from theTable
where key > (select max(key)
from theTable
where date = 8)
)
union all
select *
from theTable
where key = (select max(key)
from theTable
where key < (select min(key)
from theTable
where date = 8)
)
order by key
SELECT 'next' AS direction, MIN(date_field) AS date_key
FROM table_name
WHERE date_field > current_date
GROUP BY 1 -- necessity for group by varies from DBMS to DBMS in this context
UNION
SELECT 'prev' AS direction, MAX(date_field) AS date_key
FROM table_name
WHERE date_field < current_date
GROUP BY 1
ORDER BY 1 DESC;
Produces:
direction date_key
--------- --------
prev 3
next 19
My own attempt at the set solution, based on TheSoftwareJedi.
First question:
select date from test where date = 8
union all
select max(date) from test where date < 8
union all
select min(date) from test where date > 8
order by date;
Second question:
While debugging this, I used the data set:
(key:date) 1:1,2:3,3:8,4:8,5:19,10:19,11:67,15:45,16:8,17:3,18:1
to give this result:
select * from test2 where date = 8
union all
select * from (select * from test2
where date = (select max(date) from test2
where date < 8))
where key = (select max(key) from test2
where date = (select max(date) from test2
where date < 8))
union all
select * from (select * from test2
where date = (select min(date) from test2
where date > 8))
where key = (select min(key) from test2
where date = (select min(date) from test2
where date > 8))
order by date,key;
In both cases the final order by clause is strictly speaking optional.
If your RDBMS supports LAG and LEAD, this is straightforward (Oracle, PostgreSQL, SQL Server 2012)
These allow to choose the row either side of any given row in a single query
Try this...
SELECT TOP 3 * FROM YourTable
WHERE Col >= (SELECT MAX(Col) FROM YourTable b WHERE Col < #Parameter)
ORDER BY Col