can this be done in one sql query? - sql

table indexed on the field name
for given value of name "name1" give me that row as well as N rows before and N rows after (alphabetically)

Did it in two select statements replace the number 5 with whatever you want you N to be and change the table name and this will do it. Also replace the asterisk with correct column names. Let me know if you have any problems with this.
select * from
(
Select *
,row_number() over (order by firstname desc) as 'rowNumber'
from attendees
) as temp
where rowNumber between
(
select rownumber-1
from
(
Select *, row_number() over (order by firstname desc) as 'rowNumber'
from attendees
) as temp
where firstname = 'name1') AND (
select rownumber+1
from
(
Select *, row_number() over (order by firstname desc) as 'rowNumber'
from attendees
) as temp
where firstname = 'name1')

The following gets you the row with name = 'name4', the two rows before that, and the two rows after that.
drop table t;
create table t(
name varchar(20)
,primary key(name)
);
insert into t(name) values('name1');
insert into t(name) values('name2');
insert into t(name) values('name3');
insert into t(name) values('name4');
insert into t(name) values('name5');
insert into t(name) values('name6');
insert into t(name) values('name7');
commit;
(select name from t where name = 'name4')
union all
(select name from t where name > 'name4' order by name asc limit 2)
union all
(select name from t where name < 'name4' order by name desc limit 2);
+-------+
| name |
+-------+
| name1 |
| name2 |
| name4 |
| name5 |
| name6 |
+-------+
Edit:
Added descending order by as pointed out by cyberkiwi (otherwise I would have gotten the "first" 2 items on the wrong end).

Related

Merging two tables with no unique keys without row number

I have two tables
Table A:
Name
----
Andy
Greg
Table B:
Value
-----
1
2
I want to merge these two tables into one:
Table C:
Result
------
Andy 1
Greg 2
Note:-
without changing the order. I cannot use row numbers as I am using Apache Calcite and it doesn't support that right now.
Is this possible?
WITH X AS
(
SELECT * FROM
(
SELECT NAME AS Val1,
(SELECT Count(*) from #TableA a1 WHERE a1.Name < a2.Name) AS myRowNumber1 FROM #TableA a2
)a1
INNER JOIN
(
SELECT Id AS Val2,
(SELECT Count(*) from #TableB a1 WHERE a1.Id < a2.Id) AS myRowNumber2 FROM #TableB a2
)b1
ON a1.myRowNumber1=b1.myRowNumber2
)
SELECT Val1 +' '+ Val2 AS Result FROM X
You can use Count(*) instead of Row_Number()
OutPut:-
Result
---------
Andy 1
Greg 2
Create a new column as an identifier
SELECT *, ROW_NUMBER() OVER(PARTITION BY NAME ORDER BY NAME) AS id
INTO #NAMES
FROM TABLE1
SELECT *, ROW_NUMBER() OVER(PARTITION BY VALUE ORDER BY VALUE) AS id
INTO #VALUES_TABLE
FROM TABLE2
And then join by the row number that will be called id
SELECT *
FROM #NAMES t1
LEFT JOIN #VALUES_TABLE t2
ON t1.id = t2.id
Calcite does not have a function exactly like Oracle's ROWNUM pseudocolumn but it does have the standard SQL window function ROW_NUMBER(). You can use it as follows:
create table a as select * from (values ('Andy'), ('Greg')) as t (name);
create table b as select * from (values (1), (2)) as t (v);
select *
from (select name, row_number() over () as id from a)
join (select v, row_number() over () as id from b)
using (id);
+----+------+---+
| ID | NAME | V |
+----+------+---+
| 1 | Andy | 1 |
| 2 | Greg | 2 |
+----+------+---+
(2 rows)
If you want deterministic order, you can change over () to, say, over (order by name desc).

Get custom Distinct Values from column table

Table data and expected data as below
I wanted to select the distinct ID and Name, irrespective of branch. But I want the branch name to be displayed.
If branch need not be displayed I can use substring. Can the result be achieved using CTE.
As you don't appear to care about which 'branch' needs to be returned, you can simply use a row_number within a CTE to return just one result per ID value:
declare #t table(ID int,Name varchar(20));
insert into #t values
(10,'Manoj (CS)')
,(10,'Manoj (IS)')
,(20,'Ajay (CS)')
,(20,'AJAY (IS)')
,(30,'Sunjay(EC)')
,(40,'Lina(IS)')
,(40,'Lina(CS)')
,(40,'Lina(EC)')
,(50,'Mary(IS)')
,(50,'Mary(EC)');
with d as
(
select ID
,Name
,row_number() over (partition by ID order by Name) as rn
from #t
)
select ID
,Name
from d
where rn = 1;
Output:
+----+------------+
| ID | Name |
+----+------------+
| 10 | Manoj (CS) |
| 20 | Ajay (CS) |
| 30 | Sunjay(EC) |
| 40 | Lina(CS) |
| 50 | Mary(EC) |
+----+------------+
If you do have a preference for the (CS) branch however, you would need to alter the row_number slightly:
with d as
(
select ID
,Name
,row_number() over (partition by ID
order by case when right(Name,4) = '(CS)'
then 1
else 2
end
,Name
) as rn
from #t
)
select ID
,Name
from d
where rn = 1;
You can use row_number() function with TIES :
select top (1) with ties *
from table t
order by row_number() over (partition by id order by name);
As mentioned in the comments: Change your datamodel.
If you must live with the table as is, all you want is: all student IDs, each with one branhc/subject name arbitrarily picked. This can be achieved with a simple aggregation:
select id, min(name) from mytable group by id;

Oracle SQL, how to select first row in a group?

Here is the my SQL fiddle: http://sqlfiddle.com/#!4/75ab7/2
Basically, I have created a table and insert some data into it.
CREATE TABLE subject (
id INT NOT NULL,
seq_num INT NOT NULL,
name VARCHAR(30) NOT NULL
);
INSERT INTO subject
(id, seq_num, name)
VALUES
(1, 1, 'sub_1_1');
INSERT INTO subject
(id, seq_num, name)
VALUES
(2, 1, 'sub_1_2');
INSERT INTO subject
(id, seq_num, name)
VALUES
(3, 2,'sub_2_1');
INSERT INTO subject
(id, seq_num, name)
VALUES
(4, 2, 'sub_2_2');
INSERT INTO subject
(id, seq_num, name)
VALUES
(5, 2, 'sub_2_3');
INSERT INTO subject
(id, seq_num, name)
VALUES
(6, 3, 'sub_3_1');
INSERT INTO subject
(id, seq_num, name)
VALUES
(7, 3, 'sub_3_1');
I run this select statement:
select
LISTAGG(TRIM(id), ',') WITHIN GROUP (ORDER BY 1) AS IDS,
seq_num,
LISTAGG(TRIM(name), ',') WITHIN GROUP (ORDER BY 1) AS NAMES
from
subject
group by
seq_num
order by
seq_num asc
The select statement result:
| ids | seq_num | names |
|-------|---------|-------------------------|
| 1,2 | 1 | sub_1_1,sub_1_2 |
| 3,4,5 | 2 | sub_2_1,sub_2_2,sub_2_3 |
| 6,7 | 3 | sub_3_1,sub_3_1 |
Can I generate something like this?
| ids | seq_num | names |
|-----|---------|---------|
| 1 | 1 | sub_1_1 |
| 3 | 2 | sub_2_1 |
| 6 | 3 | sub_3_1 |
That is only pick the first row in a group.
Use row number:
select
id, seq_num, name
from
(
select id, seq_num, name,
row_number() over (partition by seq_num order by id) rn
from subject
) t
where rn = 1
order by seq_num;
Here is a link to your updated Fiddle:
Demo
You can use keep and first in oracle:
select seq_num,
max(trim(id)) keep (dense_rank first order by trim(id)) as first_id,
max(trim(name)) keep (dense_rank first order by trim(id)) as first_name
from subject
group by seq_num
order by seq_num asc;
Here is the SQL Fiddle.
This should work:
select
min(id) AS IDS,
seq_num,
min(name) AS NAMES
from
subject
group by
seq_num
order by
seq_num asc;
Working Demo
Hope it helps!

Oracle 11g: shuffling VARCHAR2 column to get random mapping

I have something like that (FIDDLE:
CREATE TABLE COMPANY(
id int primary key,
name varchar2(20)
)
I entered sample data:
+------+----------+
| ID | NAME |
+------+----------+
| 1 | John |
| 4 | Albert |
| 6 | Anna |
+------+----------+
I need to have select that returns all names and new mapped name (randomly).
I have achieved it by writing something like this:
with names as (select distinct name from company)
select oldvar, newvar
from (select rownum as id, name as oldvar from names) o,
(select rownum as id,
name as newvar
from (select name from names order by dbms_random.value)) n
where o.id = n.id
(Note that: I DON'T AND I CAN'T use COMPANY.ID )
Can anyone optimalize this SQL statement? Maybe it can be done in better, more Oracle specific way ?
You could use row_number to create a random pair:
with names1 as
(
select row_number() over (order by dbms_random.value) as rn
, name
from Company
)
, names2 as
(
select row_number() over (order by dbms_random.value) as rn
, name
from Company
)
select n1.name as name1
, n2.name as name2
from names1 n1
join names2 n2
on n1.rn = n2.rn

sql pulling a row for next or previous row of a current row

id | photo title | created_date
XEi43 | my family | 2009 08 04
dDls | friends group | 2009 08 05
32kJ | beautiful place | 2009 08 06
EOIk | working late | 2009 08 07
Say I have the id 32kJ. How would I get the next row or the previous one?
This is what I use for finding previous/next records. Any column in your table can be used as the sort column, and no joins or nasty hacks are required:
Next record (date greater than current record):
SELECT id, title, MIN(created) AS created_date
FROM photo
WHERE created >
(SELECT created FROM photo WHERE id = '32kJ')
GROUP BY created
ORDER BY created ASC
LIMIT 1;
Previous record (date less than current record):
SELECT id, title, MAX(created) AS created_date
FROM photo
WHERE created <
(SELECT created FROM photo WHERE id = '32kJ')
GROUP BY created
ORDER BY created DESC
LIMIT 1;
Example:
CREATE TABLE `photo` (
`id` VARCHAR(5) NOT NULL,
`title` VARCHAR(255) NOT NULL,
`created` DATETIME NOT NULL,
INDEX `created` (`created` ASC),
PRIMARY KEY (`id`)
)
ENGINE = InnoDB;
INSERT INTO `photo` (`id`, `title`, `created`) VALUES ('XEi43', 'my family', '2009-08-04');
INSERT INTO `photo` (`id`, `title`, `created`) VALUES ('dDls', 'friends group', '2009-08-05');
INSERT INTO `photo` (`id`, `title`, `created`) VALUES ('32kJ', 'beautiful place', '2009-08-06');
INSERT INTO `photo` (`id`, `title`, `created`) VALUES ('EOIk', 'working late', '2009-08-07');
SELECT * FROM photo ORDER BY created;
+-------+-----------------+---------------------+
| id | title | created |
+-------+-----------------+---------------------+
| XEi43 | my family | 2009-08-04 00:00:00 |
| dDls | friends group | 2009-08-05 00:00:00 |
| 32kJ | beautiful place | 2009-08-06 00:00:00 |
| EOIk | working late | 2009-08-07 00:00:00 |
+-------+-----------------+---------------------+
SELECT id, title, MIN(created) AS next_date
FROM photo
WHERE created >
(SELECT created FROM photo WHERE id = '32kJ')
GROUP BY created
ORDER BY created ASC
LIMIT 1;
+------+--------------+---------------------+
| id | title | next_date |
+------+--------------+---------------------+
| EOIk | working late | 2009-08-07 00:00:00 |
+------+--------------+---------------------+
SELECT id, title, MAX(created) AS prev_date
FROM photo
WHERE created <
(SELECT created FROM photo WHERE id = '32kJ')
GROUP BY created
ORDER BY created DESC
LIMIT 1;
+------+---------------+---------------------+
| id | title | prev_date |
+------+---------------+---------------------+
| dDls | friends group | 2009-08-05 00:00:00 |
+------+---------------+---------------------+
I realize that you are using MySQL, but just for reference, here is how you would do this using Oracle's analytic functions LEAD and LAG:
select empno, ename, job,
lag(ename, 1) over (order by ename) as the_guy_above_me,
lead(ename, 2) over (order by ename) as the_guy_two_rows_below_me
from emp
order by ename
I guess there's a reason why Oracle costs money and MySQL is free... :-)
This page shows you how to emulate analytic functions in MySQL.
Did you want the next/previous row by date? If so, you could do this:
select MyTable.*
from MyTable
join
(select id
from MyTable
where created_date < (select created_date from MyTable where id = '32kJ')
order by created_date desc, id desc
limit 1
) LimitedTable on LimitedTable.id = MyTable.fund_id;
Using Mike's MAX/MIN trick we can make previous\next jumps for all sorts of things. This msAccess example will return the previous close for every record in a stock market data table. Note: the '<=' is for weekends and holidays.
SELECT
tableName.Date,
tableName.Close,
(SELECT Close
FROM tableName
WHERE Date = (SELECT MAX(Date) FROM tableName
WHERE Date <= iJoined.yesterday)
) AS previousClose
FROM
(SELECT Date, DateAdd("d",-1, Date) AS yesterday FROM tableName)
AS iJoined
INNER JOIN
tableName ON tableName.Date=iJoined.Date;
...'yesterday' demonstrates using a function(Date-1) jump; we could have simply used...
(SELECT Date FROM tableName) AS iJoined
/* previous record */
(SELECT MAX(Date) FROM tableName WHERE Date < iJoined.Date)
/* next record */
(SELECT MIN(Date) FROM tableName WHERE Date > iJoined.Date)
The trick is we can previous\next # of whatever(s) with MAX\MIN and a jump function()
Horrible hack - I don't like this but might work..
with yourresult as
(
select id, photo_title, created_date, ROW_NUMBER() over(order by created_date) as 'RowNum' from your_table
)
-- Previous
select * from yourresult where RowNum = ((select RowNum from yourresult where id = '32kJ') -1)
-- Next
select * from yourresult where RowNum = ((select RowNum from yourresult where id = '32kJ') +1)
That of any use?
I considered id as primary key in the table (and as "row number"), and used it to compare each record with the record before.
The following code must work.
CREATE SCHEMA temp
create table temp.emp (id integer,name varchar(50), salary varchar(50));
insert into temp.emp values(1,'a','25000');
insert into temp.emp values(2,'b','30000');
insert into temp.emp values(3,'c','35000');
insert into temp.emp values(4,'d','40000');
insert into temp.emp values(5,'e','45000');
insert into temp.emp values(6,'f','20000');
select * from temp.emp
SELECT
current.id, current.name, current.salary,
case
when current.id = 1 then current.salary
else
case
when current.salary > previous.salary then previous.salary
else current.salary
end
end
FROM
temp.emp AS current
LEFT OUTER JOIN temp.emp AS previous
ON current.id = previous.id + 1