SQL: Limit by unknown number of occurences - sql

Having a SQL table, consistent of the columns id and type. I Want to select only the first occurences of a type without using WHERE, since i dont know which types wild occur first, and without LIMIT since i don't know how many.
id | type
---------
1 | 1
2 | 1
3 | 2
4 | 2
5 | 2
E.g.:
SELECT id FROM table ORDER BY type (+ ?) should only return id 1 and 2
SELECT id FROM table ORDER BY type DESC (+ ?) should only return id 3, 4 and 5
Can this be acheived via standard and simple SQL operators?

That's easy. You must use a where clause and evaluate the minimum type there.
SELECT *
FROM mytable
WHERE type = (select min(type) from mytable)
ORDER BY id;
EDIT: Do the same with max() if you want to get the maximum type records.
EDIT: In case the types are not ascending as in your example, you will have to get the type of the minimum/maximum id instead of getting the minimum/maximum type:
SELECT *
FROM mytable
WHERE type = (select type from mytable where id = (select min(id) from mytable))
ORDER BY id;

Related

if count value of the column is greater than 1, I want to print the count of the column else I want to print value in the field

I am writing a query which fetches details from different tables. In one column I want to print count value of a column. If the count value of the column is greater than 1, I want to print the count of the column else I want to print value in the field.
I want to build a query which will give me count of user_id from table 1 & 2. if the count user_id is greater than 1, then print count (user_id) else print value of user_id
Table:1
| user_id |
| John |
| Bob |
| Kris |
| Tom |
Table:2
| user_id |
| Rob |
query result should list count of table1 as it greater than 1. Table2 should list Rob as it is lesser than 2
You want to select user IDs (names actually) from a table. If it's just one row then show that name, otherwise show the number of entries instead. So, just use a CASE expression to check whether count is 1 or greater than 1.
You probably need CAST or CONVERT to turn the count number into a string, so the CASE expression always returns the same type (this is how CASE works).
select
case when count(*) > 1
then cast(count(*) as varchar(100))
else max(user_id)
end as name_or_count
from mytable
Window Functions come to mind but since your user_ids are not numbers, you'll run into an issue where you can't have two different data types in the same column. See how this works for you. Make sure to cast the varchar numbers back to integer if this script is part of a larger process.
with cte as
(select 'John' as user_id union all
select 'Bob' as user_id union all
select 'Kris' as user_id union all
select 'Tom' as user_id)
select distinct case when count(*) over() > 1
then cast(count(*) over() as varchar) else user_id end
from cte
with cte as
(select 'Rob' as user_id)
select distinct case when count(*) over() > 1
then cast(count(*) over() as varchar) else user_id end
from cte

Select Table records in given order - Oracle

I have table lets say - Students, with 5 records and id(s) are 1 to 5, now i want to select the records - in a way that result should come like given sorting order of id column
id column should be resulted - 5,2,1,3,4 ( order may vary each time)
is there any other way to do this in oracle sql?
In mysql we have FIELD() clause to do this. I want to achieve this in oracle.
A quick and dirty way uses instr():
order by instr(',5,2,1,3,4,', ',' || id ',')
To handle values not in the string, you can convert them to NULL and sort those accordingly:
order by nullif(instr(',5,2,1,3,4,', ',' || id ','), 0) nulls last
Oracle uses heap-oraganized table, which means by default rows are stored in no particular order. However, while selecting the rows, you could mention ORDER BY clause explicitly to return the result set in ASC(default)/DESC order.
You could use DECODE/CASE to customize the sorting, for example:
with data as(
select level num from dual connect by level <=5
)
select num
from data
order by case
when num = 5 then 1
when num = 2 then 2
when num = 1 then 3
when num = 3 then 4
when num = 4 then 5
end;
NUM
----------
5
2
1
3
4

Sort by one column, but get offset by another

Let's say I have a table with two columns:
| ID | A |
I want to sort by A, then get the 10 records after a given ID. What would be the best way to handle this in Postgres?
To clarify, I want to sort by A, but do my pagination by the ID. So if I had a table like:
1 | 'C'
2 | 'B'
3 | 'A'
4 | 'G'
5 | 'A'
6 | 'H'
So after sorting by A, I'd want the first three values after id=1, so:
1 | 'C'
4 | 'G'
6 | 'H'
An ordering of any column is purely dependant on the order by clause and the terms "before" or "after" come into picture only when there's a pre-determined order. So, once the records are ordered by column "A", there's no guarantee that the id's will be ordered in the sequence 1,4,6, unless you also specified that ordering of id.
So, if you
want the first three values after id=1
It means there should be a way to determine the point where the id value has become 1 and all the rows beyond are to be considered. To ensure that you have to explicitly include id in the order by. A COUNT analytic function can come to our rescue to mark the point.
SELECT id,a
FROM ( SELECT t.*,COUNT(CASE WHEN id = 1 THEN 1 END) --the :id argument
OVER( ORDER BY a,id) se
FROM t order by a,id --the rows are ordered first by a, then by id
-- same as in the above count analytic function
) s
WHERE se = 1 limit 3; -- the argument 3 or 10 that you wish to pass
-- se = 1 won't change for other ids, it's a marker
-- that id = n is reached
DEMO
I think this will do:
SELECT *
FROM (SELECT * from MyTable where ID > givenId order by A) sub
LIMIT 10;
You don't want the A columns, so:
SELECT r.*
FROM t
WHERE t.id > ANY (SELET id FROM t t2 WHERE t2.col = 'A')
ORDER BY col
LIMIT 10;
Note that this does not return any rows with A as the value. It also works when the comparison value is not sorted first.
this will work:
SELECT * from Table1 where "ID"=1
order by "A" desc limit 2;
check :http://sqlfiddle.com/#!15/5854b/3
for your query :
SELECT * from Table1 where "ID"=1
order by "A" desc limit 10;

How to efficiently get a value from the last row in bulk on SQL Server

I have a table like so
Id | Type | Value
--------------------
0 | Big | 2
1 | Big | 3
2 | Small | 3
3 | Small | 3
I would like to get a table like this
Type | Last Value
--------------------
Small | 3
Big | 3
How can I do this. I understand there is an SQL Server method called LAST_VALUE(...) OVER .(..) but I can't get this to work with GROUP BY.
I've also tried using SELECT MAX(ID) & SELECT TOP 1.. but this seems a bit inefficient since there would be a subquery for each value. The queries take too long when the table has a few million rows in it.
Is there a way to quickly get the last value for these, perhaps using LAST_VALUE?
You can do it using rownumber:
select
type,
value
from
(
select
type,
value,
rownumber() over (partition by type order by id desc) as RN
) TMP
where RN = 1
Can't test this now since SQL Fiddle doesn't seem to work, but hopefully that's ok.
The most efficient method might be not exists, which uses an anti-join for the underlying operator:
select type, value
from likeso l
where not exists (select 1 from likeso l2 where l2.type = l.type and l2.id > l.id)
For performance, you want an index on likeso(type, id).
I really wonder if there is more efficent solution but, I use following query on such needs;
Select Id, Type, Value
From ( Select *, Max (Id) Over (Partition By Type) As LastId
From #Table) T
Where Id = LastId

Trouble performing Postgres group by non-ID column to get ID containing max value

I'm attempting to perform a GROUP BY on a join table table. The join table essentially looks like:
CREATE TABLE user_foos (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
foo_id INT NOT NULL,
effective_at DATETIME NOT NULL
);
ALTER TABLE user_foos
ADD CONSTRAINT user_foos_uniqueness
UNIQUE (user_id, foo_id, effective_at);
I'd like to query this table to find all records where the effective_at is the max value for any pair of user_id, foo_id given. I've tried the following:
SELECT "user_foos"."id",
"user_foos"."user_id",
"user_foos"."foo_id",
max("user_foos"."effective_at")
FROM "user_foos"
GROUP BY "user_foos"."user_id", "user_foos"."foo_id";
Unfortunately, this results in the error:
column "user_foos.id" must appear in the GROUP BY clause or be used in an aggregate function
I understand that the problem relates to "id" not being used in an aggregate function and that the DB doesn't know what to do if it finds multiple records with differing ID's, but I know this could never happen due to my trinary primary key across those columns (user_id, foo_id, and effective_at).
To work around this, I also tried a number of other variants such as using the first_value window function on the id:
SELECT first_value("user_foos"."id"),
"user_foos"."user_id",
"user_foos"."foo_id",
max("user_foos"."effective_at")
FROM "user_foos"
GROUP BY "user_foos"."user_id", "user_foos"."foo_id";
and:
SELECT first_value("user_foos"."id")
FROM "user_foos"
GROUP BY "user_foos"."user_id", "user_foos"."foo_id"
HAVING "user_foos"."effective_at" = max("user_foos"."effective_at")
Unfortunately, these both result in a different error:
window function call requires an OVER clause
Ideally, my goal is to fetch ALL matching id's so that I can use it in a subquery to fetch the legitimate full row data from this table for matching records. Can anyone provide insight on how I can get this working?
Postgres has a very nice feature called distinct on, which can be used in this case:
SELECT DISTINCT ON (uf."user_id", uf."foo_id") uf.*
FROM "user_foos" uf
ORDER BY uf."user_id", uf."foo_id", uf."effective_at" DESC;
It returns the first row in a group, based on the values in parentheses. The order by clause needs to include these values as well as a third column for determining which is the first row in the group.
Try:
SELECT *
FROM (
SELECT t.*,
row_number() OVER( partition by user_id, foo_id ORDER BY effective_at DESC ) x
FROM user_foos t
)
WHERE x = 1
If you don't want to use a sub query based on a composite of all three keys then you need to create a "dense rank" window function field that orders subsets of id, user_id and foo_id by effective date with the rank order field. Then subquery that and take the records where rank_order=1. Since the rank ordering was by effective date you are getting all fields of the record with the highest effective date for each foo and user.
DATSET
1 1 1 01/01/2001
2 1 1 01/01/2002
3 1 1 01/01/2003
4 1 2 01/01/2001
5 2 1 01/01/2001
DATSET WITH RANK ORDER PARTITIONED BY FOO_ID, USER_ID ORDERED BY DATE DESC
1 3 1 1 01/01/2001
2 2 1 1 01/01/2002
3 1 1 1 01/01/2003
4 1 1 2 01/01/2001
5 1 2 1 01/01/2001
SELECT * FROM QUERY ABOVE WHERE RANK_ORDER=1
3 1 1 1 01/01/2003
4 1 1 2 01/01/2001
5 1 2 1 01/01/2001