SQL query to select equal or less than or greater than - sql

Assuming that there is a table t with the following columns: Code int, Name nvarchar(50).
I'd would like to query the table for the most matching row for a given Code c. The 'most matching' criteria (in order of importance):
1) select a row whose Code matches c
2) select a row whose Code is greater than c (but the very first one). For example, if c = 4 and t
contains 1, 2, 3, 5, 6, and 7, I'd like to select 5.
3) select a row whose
Code is less than c. For example, if c = 4 and t contains 3, 2, and
1, I'd like to select 3.
The code is going to be in a stored procedure.
Could someone please suggest how to accomplish the above.
Thanks.
Sample data and expected results:
1, "Name1"
2, "Name2"
4, "Name4"
5, "Name5"
If c=2, result: 2,"Name2"
If c=3, result: 4,"Name4"
if c=6, result: 5,"Name5"

I'd order the rows by two criteria - the absolute distance from the target number and whether it's greater or lesser than it, and just pick the top row. E.g., assuming the target code is 4:
SELECT TOP 1 *
FROM t
ORDER BY ABS(code - 4) ASC, CASE WHEN code > 4 THEN 1 ELSE 0 END DESC

That is a top 1 query; you want the one best matching record. So select TOP 1 along the desired order in ORDER BY.
select top 1 *
from mytable
order by
case when code = #code then 1
when code > #code then 2
else 3
end,
abs(code - #code);

Related

How to check the possibility of groups union in a sequence order

I have a table with the following columns:
ID_group, ID_elements
For example with the following records:
1, 1
1, 2
2, 2
2, 4
2, 5
2, 6
3, 7
And I have sets of the elements, for example: 1,2,5; 1,5,2; 1,2,4; 2,7;
I need to check (true or false) that exist a common group for the pairs of adjacent elements.
For example elements:
1,2,5 -> true [i.e. elements 1,2 has common group 1 and elements 2,5 has common group 2]
1,5,2 -> false [i.e. 1,5 do not have a common group unlike 5,2 (but the result is false due to 1,5 - false)]
1,2,4 -> true
2,7 -> false
First, we need a list of pairs. We can get this by taking your set as an array, turning each element into a row with unnest and then making pairs by matching each row with its previous row using lag.
with nums as (
select *
from unnest(array[1,2,5]) i
)
select lag(i) over() a, i b
from nums
offset 1;
a | b
---+---
1 | 2
2 | 5
(2 rows)
Then we join each pair with each matching row. To avoid counting duplicate data rows twice, we count only the distinct rows.
with nums as (
select *
from unnest(array[1,2,5]) i
), pairs as (
select lag(i) over() a, i b
from nums
offset 1
)
select
count(distinct(id_group,id_elements)) = (select count(*) from pairs)
from pairs
join foo on foo.id_group = a and foo.id_elements = b;
This works on any size array.
dbfiddle
Your query to check if elements in a set evaluate to true or not can be done via procedures/function. Set representation can be taken as a string and then splitting it to substring then returning the required result can use a record for multiple entries. For sql query, below is a sample that can be used as a workaround, you can try changing the below query based on your requirement.
select case when ( Select count(*)
from ( SELECT
id_group, count(distinct id_elements)
from table where
id_group
in (1,2,5)
group by ID_group having
id_elements
in (1,2,5)) =3 ) then "true" else "false"
end) from table;
#Schwern, thank you, it helped. But I have changed the condition join foo on foo.id_group = a, because as I understand, a is element's ID, not group's. I have changed the following section:
join foo fA on fA.id_elements = a
join foo fB on fB.id_elements = b and fA.group_id = fB.group_id;

Top 1 & Top 1 1 ( Expr1000 1 ) typical output

SELECT TOP 1 * FROM Customers;
This gives me all customer details of first row
( customerid, customer name etc..,)
SELECT TOP 1 1 FROM Customers;
I got o/p as
Expr1000
------------
1
But I genuinely don't understand above output, and I searched for it but couldn't get the clear understanding of it.
If your first query * mean show all fields
But:
SELECT TOP 1 1 FROM Customers;
is equal to:
SELECT TOP 1
1 as myConstantField FROM Customers;
So you create a single constant field and you will get 1 for every row in Customers, but TOP 1 will filter to only the first one
The second "1" is not considered as a column, it's just a constant. So with this query, you ask to write down one (TOP 1) row with one column with the constant "1". Same thing will happen if you write something like this :
SELECT TOP 1 'Hey you', 1 as valueone ,2 ,3, customerid
FROM Customers ;
You'll have one row with the values 'Hey you' , '1' in column "valueone", '2', '3' then your customerid from the first row
When you
SELECT 1
it will show just one row of 1. When you
SELECT 1 FROM Customers
it will give you the number of rows that there in Customers. So when you
SELECT TOP 1 1 FROM Customers
it will give you only the first row of 1 as if you did
SELECT TOP 1 * FROM Customers

Select postgresql for combinations

I have the following 2 tables:
SQL Tables
I have a list of ids (1,2,3,4,5,6,7,8, ..., 10000).
Unique combination of those ids is inserted into another table.
So, how can I find these inique combination If I pass the list of ids to search.
E.g., I search for ARRAY([2,3,4]). The combination exisst only for the unique_combnation 1, so the result will be as follows:
1 3
1 2
1 4
There is no any unique_comb which contains ids ARRAY([2,3,4]).
If I search for [1,4], the results will be as follows:
1 3
1 2
1 4
2 2
2 4
2 5
How can I do it? I know how to do it in a bad way:
CREATE TEMPORARY TABLE t1
Iterate over given ids: SELECT * FROM .. where id = ANY(ARRAY[1,4]) and get all rows, insert into t1 all rows.
Then group everything by unique_comb.
Then count the number of groups. If the number of unique combinations is not more than 1, then return the id of the unique combination, else (unique combinations > 1) return nothing
Is it a way to make it with 1-2 sql lines? I am using postgresql 9.3
select unique_comb t2 where id = ANY(ARRAY[1, 4]) group by unique_comb ...
The answer below is correct. I have modifed just a little bit the query and it began to work.
It will choose several ids from table unique thing.
The result will be of select unique_comb, array_agg(id) t2 where id = ANY(ARRAY[1, 4]) group by unique_comb will be as follows:
The process that you describe is seems to be something like a group by:
select unique_comb
from t2
where id = ANY(ARRAY[1, 4])
group by unique_comb
having count(*) = array_length(ARRAY[1, 4], 1);
For big tables, and long arrays (not for your examples with just 2 or 3 elements), a more sophisticated query with a recursive CTE would be faster.
In any case you need an index on (id, unique_comb) - in this order! A primary key serves nicely.
WITH RECURSIVE cte AS (
SELECT unique_comb, id, 2 AS i -- start with index for 2nd array elem
FROM tbl
WHERE id = 5 -- *first* element array
UNION ALL
SELECT t.unique_comb, t.id, c.i + 1
FROM cte c
JOIN tbl t USING (unique_comb)
WHERE t.id = ('{5, ... long array ... , 4}'::int[])[c.i] -- your array here
)
SELECT unique_comb
FROM cte
WHERE id = 4; -- *last* element of array
The advantage of this approach is to rule out most (or all) rows early in the game. If you have information on value frequencies, you would put the rarest elements first.

SQL Rearrange row order

Problem:
A row in my_table contains
1
2
3
3
4
4
4
I want to rearrange the row order to the following
4
4
4
1
3
3
2
Is there any way I can perform this?
I have not written any code so far. ( I do not know how to begin)
Just use CASE in the ORDER BY - it's much cleaner and easier to read.
...
ORDER BY CASE WHEN YourColumn = 4 then 0
WHEN YourColumn = 2 then 1
ELSE 2 END, yourcolumn
It's also a bad idea to change the order of the rows since there is actually no inherent order in SQL - it's all in the presentation layer, so use ORDER BY in your SELECTs to accomplish it.
as Adam Wenger said,
or if you wanted, it could be done in your table, meaning your sql statement can remain as a basic select * from table order by newcolumn*
*see below
add a new column
update each row set column=1 where original_column=4
update each row set column=2 where original_column=2
update each row set column=3 where original_column=1
update each row set column=4 where original_column=3
and then order by this new column.
Create a lookup table with the original value and the corresponding sort order:
SortLookupTable:
OriginalValue, SortOrder
1, 2
2, 4
3, 3
4, 1
Then join the table to your original table
SELECT T.*
FROM
MyTable T
INNER JOIN SortLookupTable S
ON T.Value = S.OriginalValue
ORDER BY
S.SortOrder;
SELECT yourColumn
FROM
(
SELECT yourColumn
, CASE yourColumn
WHEN 4 THEN 1
WHEN 1 THEN 2
WHEN 3 THEN 3
WHEN 2 THEN 4
END AS SortColumn
FROM yourTable
) AS t
ORDER BY t.SortColumn ASC
This will sort 4, 1, 3, 2

Special order by on SQL query

I need to display a list of records from a database table ordered by some numeric column. The table looks like this:
CREATE TABLE items (
position int NOT NULL,
name varchar(100) NOT NULL,
);
INSERT INTO items (position, name) VALUE
(1, 'first'),
(5, 'second'),
(8, 'third'),
(9, 'fourth'),
(15, 'fifth'),
(20, 'sixth');
Now, the order of the list should change according to a parameter provided by the user. This parameter specifies which record comes first like this:
position = 0
order should be = 1, 5, 8, 9, 15, 20
position = 1
order should be = 20, 1, 5, 8, 9, 15
position = 2
order should be = 15, 20, 1, 5, 8, 9
In other words the last record becomes the first and so on. Can you think of a way to do this in SQL?
I'm using MySQL but an example in any SQL database will do.
Thanks
See how this works for you. Uses generic SQL so it should be valid for MySql (untested) as well.
DECLARE #user_sort INTEGER
SET #user_sort = 0
SELECT position, name FROM
(
SELECT I1.position, I1.name, COUNT(*) AS rownumber, (SELECT COUNT(*) FROM items) AS maxrows
FROM items I1, items I2
WHERE I2.position <= I1.position
GROUP BY I1.position, I1.name
) Q1
ORDER BY
CASE WHEN maxrows - rownumber < (#user_sort % maxrows) THEN 1 ELSE 2 END, position
Note:
* If the user provided sort index is greater than the row count, the value will wrap to within the valid range. To remove this functionality, remove the "% maxrows" from the ORDER BY.
Results:
SET #user_sort = 0
position name
1 first
5 second
8 third
9 fourth
15 fifth
20 sixth
SET #user_sort = 1
position name
20 sixth
1 first
5 second
8 third
9 fourth
15 fifth
SET #user_sort = 2
position name
15 fifth
20 sixth
1 first
5 second
8 third
9 fourth
SET #user_sort = 9
9 fourth
15 fifth
20 sixth
1 first
5 second
8 third
Are you sure you want to do this in SQL?
To me, this sounds like you should load the results in a dataset of some sort, and then either re-order them as you want, or position the starting point at the correct position.
Possibly using a linked list.
ORDER BY (FIELD(position, 1, 5, 8, 9, 15, 20) + parameter) % 7
Edit: To make the peanut gallery happy, the general solution is:
ORDER BY (SELECT ix + parameter - 1 FROM (SELECT i.position, #ix := #ix + 1 AS ix FROM (SELECT #ix := 0) AS n, items AS i ORDER BY position) AS s WHERE s.position = items.position) % (SELECT COUNT(*) FROM items)
I'm riffing on beach's solution here, but eliminating the self-join and only selecting from the items table twice (and using Oracle syntax):
select
i.position
, i.name
from(
select
items.*
, ( SELECT COUNT(*) FROM items ) AS maxrows
from items
order by position
) i
order by
case
when rownum > maxrows - 2 -- NOTE: change "2" to your "position" variable
then 1 - 1 / rownum -- pseudo-rownum < 1, still ascending
else
rownum
end
;
If it's a set list that you know the number of items you could do something like:
SELECT *
FROM Items
ORDER BY CASE WHEN Position >= Position THEN POSITION ELSE Position+1000 END
But its really ugly.
This really is not an ideal thing to be doing in SQL.
I have solution, but with large tables it will be slow.
DECLARE #iOrder INT
SET #iOrder = 4
SELECT abc.position,abc.name FROM
(
SELECT position,name,ROW_NUMBER() OVER (ORDER BY position) AS rownum
FROM items
) abc
WHERE abc.rownum >= #iOrder
UNION ALL
SELECT def.position, def.name FROM
(
SELECT position,name,ROW_NUMBER() OVER (ORDER BY position) AS rownum
FROM items
) def
WHERE def.rownum < #iOrder
Note that the use of UNION (Without the all) will reorder the results as it'll be looking for duplicates
As per John's comment, but with LIMIT syntax instead (ROW_NUMBER/OVER doesn't work in MySQL and besides LIMIT is much easier to read):
(
SELECT position, name FROM items
ORDER BY position
LIMIT #offset, #bignum
) UNION ALL (
SELECT position, name FROM items
ORDER BY position
LIMIT #offset
)
Where #bignum is an arbitrary number higher than any number of results you might have.
I'm still not wholly convinced this will in practice be faster than rearranging the list on the web server... would depend exactly how you were dealing with the result set and how big it was, I suppose. But at least it avoids the self-cross-join involved in beach's clever approach.