SQL SELECT Sort by specific row - sql

Suppose I have a table food, which I query SELECT name FROM food ORDER BY name:
| name
|--------
| Apple
| Banana
| Carrot
| Donut
...
I wish to specify that specific items of my choice be pinned to the top (e.g. "Lemon" and then "Carrot"), if they are in the table. Like so:
| name
| -------
| Lemon
| Carrot
| Apple
| Banana
| Donut
...
What kind of SQL query can I use to get this specific sort?

You can use a case statement in your order by clause to prioritize items with a certain name.
The following will put Lemon and Carrot in priority order by assigning them the values of 1 and 2 from the case, where all others will get the value 3. Those remaining that were assigned 3 will then be sorted by the second expression in the order by clause, which is just the name column.
SELECT *
FROM food
ORDER BY
CASE name
WHEN 'Lemon' THEN 1
WHEN 'Carrot' THEN 2
ELSE 3
END,
name

Make a look up table like this:
WITH odering(F, Ord) AS
(
VALUES ('Lemmon', 2),
('Carrot', 1)
)
SELECT name
FROM table t
LEFT JOIN ordering Ord on T.name = Ord.name
ORDER BY COALESCE(Ord.Ord, 0) DESC, Name ASC

Chances are, this is going to be RDBMS specific. The one being used is not specified.
MySQL, commonly used, provides a function field(). Good for custom sorts on bounded values.
mysql> create temporary table food as select 'Apple' as food union select 'Banana' union select 'Carrot' union select 'Donut';
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select * from food;
+--------+
| food |
+--------+
| Apple |
| Banana |
| Carrot |
| Donut |
+--------+
4 rows in set (0.00 sec)
mysql> select * from food order by field (food, 'Carrot', 'Apple', 'Banana', 'Donut');
+--------+
| food |
+--------+
| Carrot |
| Apple |
| Banana |
| Donut |
+--------+
4 rows in set (0.00 sec)

I'd use CTE and go like this:
WITH FoodOrder(name, foodOrder)
AS
(
SELECT name
, foodOrder = CASE name
WHEN 'Lemon' THEN 200
WHEN 'Carrot' THEN 100
ELSE 0
END
FROM food
)
SELECT name
FROM FoodOrder
ORDER BY foodOrder DESC, name
Please note that if you will choose enough spacing between CASE values (0, 100 ,200) you can easily add another prioritized food afterwards if needed without overwriting already existing values.

Related

SQL to count table column

I have a single table as shown below:
ID | Name | Category
--------------------
1 | Cat | Mammal
2 | Dog | Mammal
3 | Pea | Vegetable
4 | Snake| Reptile
I would like an SQL query that will list each individual item with the count of the elements in its category. i.e.
Name | Count of Category
-------------------------
Cat | 2
Dog | 2
Pea | 1
Snake | 1
Edit1: I am using postgrsql
If your DBMS support window function, we can try to use COUNT window function and add each individual item in PARTITION BY
SELECT Name,COUNT(*) OVER(PARTITION BY Category)
FROM T
sqlfiddle
If possible, I would also prefer a window function like D-Shih showed. If your DB doesn't support this, you can use a subquery to count the category, something like this:
SELECT name, c "Count of Category" FROM
yourtable y JOIN
(SELECT category, COUNT(category) c FROM yourtable GROUP BY category) sub
ON y.category = sub.category;
This will produce the identic outcome.

Counting SQLite rows that might match multiple times in a single query

I have a SQLite table which has a column containing categories that each row may fall into. Each row has a unique ID, but may fall into zero, one, or more categories, for example:
|-------+-------|
| name | cats |
|-------+-------|
| xyzzy | a b c |
| plugh | b |
| quux | |
| quuux | a c |
|-------+-------|
I'd like to obtain counts of how many items are in each category. In other words, output like this:
|------------+-------|
| categories | total |
|------------+-------|
| a | 2 |
| b | 2 |
| c | 2 |
| none | 1 |
|------------+-------|
I tried to use the case statement like this:
select case
when cats like "%a%" then 'a'
when cats like "%b%" then 'b'
when cats like "%c%" then 'c'
else 'none'
end as categories,
count(*)
from test
group by categories
But the problem is this only counts each row once, so it can't handle multiple categories. You then get this output instead:
|------------+-------|
| categories | total |
|------------+-------|
| a | 2 |
| b | 1 |
| none | 1 |
|------------+-------|
One possibility is to use as many union statements as you have categories:
select case
when cats like "%a%" then 'a'
end as categories, count(*)
from test
group by categories
union
select case
when cats like "%b%" then 'b'
end as categories, count(*)
from test
group by categories
union
...
but this seems really ugly and the opposite of DRY.
Is there a better way?
Fix your data structure! You should have a table with one row per name and per category:
create table nameCategories (
name varchar(255),
category varchar(255)
);
Then your query would be easy:
select category, count(*)
from namecategories
group by category;
Why is your data structure bad? Here are some reasons:
A column should contain a single value.
SQL has pretty lousy string functionality.
SQL queries to do what you want cannot be optimized.
SQL has a great data structure for storing lists. It is called a table, not a string.
With that in mind, here is one brute force method for doing what you want:
with categories as (
select 'a' as category union all
select 'b' union all
. . .
)
select c.category, count(t.category)
from categories c left join
test t
on ' ' || t.categories || ' ' like '% ' || c.category || ' %'
group by c.category;
If you already have a table of valid categories, then the CTE is not needed.

Splitting a string column in BigQuery

Let's say I have a table in BigQuery containing 2 columns. The first column represents a name, and the second is a delimited list of values, of arbitrary length. Example:
Name | Scores
-----+-------
Bob |10;20;20
Sue |14;12;19;90
Joe |30;15
I want to transform into columns where the first is the name, and the second is a single score value, like so:
Name,Score
Bob,10
Bob,20
Bob,20
Sue,14
Sue,12
Sue,19
Sue,90
Joe,30
Joe,15
Can this be done in BigQuery alone?
Good news everyone! BigQuery can now SPLIT()!
Look at "find all two word phrases that appear in more than one row in a dataset".
There is no current way to split() a value in BigQuery to generate multiple rows from a string, but you could use a regular expression to look for the commas and find the first value. Then run a similar query to find the 2nd value, and so on. They can all be merged into only one query, using the pattern presented in the above example (UNION through commas).
Trying to rewrite Elad Ben Akoune's answer in Standart SQL, the query becomes like this;
WITH name_score AS (
SELECT Name, split(Scores,';') AS Score
FROM (
(SELECT * FROM (SELECT 'Bob' AS Name ,'10;20;20' AS Scores))
UNION ALL
(SELECT * FROM (SELECT 'Sue' AS Name ,'14;12;19;90' AS Scores))
UNION ALL
(SELECT * FROM (SELECT 'Joe' AS Name ,'30;15' AS Scores))
))
SELECT name, score
FROM name_score
CROSS JOIN UNNEST(name_score.score) AS score;
And this outputs;
+------+-------+
| name | score |
+------+-------+
| Bob | 10 |
| Bob | 20 |
| Bob | 20 |
| Sue | 14 |
| Sue | 12 |
| Sue | 19 |
| Sue | 90 |
| Joe | 30 |
| Joe | 15 |
+------+-------+
If someone is still looking for an answer
select Name,split(Scores,';') as Score
from (
# replace the inner custome select with your source table
select *
from
(select 'Bob' as Name ,'10;20;20' as Scores),
(select 'Sue' as Name ,'14;12;19;90' as Scores),
(select 'Joe' as Name ,'30;15' as Scores)
);

Sybase SQL Select Distinct Based on Multiple Columns with an ID

I'm trying to query a sybase server to get examples of different types of data we hold for testing purposes.
I have a table that looks like the below (abstracted)
Animals table:
id | type | breed | name
------------------------------------
1 | dog | german shepard | Bernie
2 | dog | german shepard | James
3 | dog | husky | Laura
4 | cat | british blue | Mr Fluffles
5 | cat | other | Laserchild
6 | cat | british blue | Sleepy head
7 | fish | goldfish | Goldie
As I mentioned I want an example of each type so for the above table would like a results set like (in reality I just want the ID's):
id | type | breed
---------------------------
1 | dog | german shepard
3 | dog | husky
4 | cat | british blue
5 | cat | other
7 | fish | goldfish
I've tried multiple combinations of queries such as the below but they are either invalid SQL (for sybase) or return invalid results
SELECT id, DISTINCT ON type, breed FROM animals
SELECT id, DISTINCT(type, breed) FROM animals
SELECT id FROM animals GROUP BY type, breed
I've found other questions such as SELECT DISTINCT on one column but this only deal with one column
Do you have any idea how to implement this query?
Maybe you have to use aggregate function max or min for column ID. It will return only one ID for grouped columns.
select max(Id), type, breed
from animals
group by type, breed
EDIT:
Other different ways to do it:
With having and aggregate function
select id, type, breed
from animals
group by type, breed
having id = max(Id)
With having and aggregate subquery
select id, type, breed
from animals a1
group by type, breed
having id = (
select max(id)
from animals a2
where a2.type = a1.type
and a2.breed = a1.breed
)
Try this and let me know if it works:
select distinct breed, max(id) as id , max(type) as type
from animals
You may have to play around with max()
The arbitrary choice here is max(), but you could arbitrarily use min() instead.
max() returns the largest value for that columns, min() the smallest

how to limit the amount of sql data that i want to display?

example of what i have in my database :
sub-category | item
-----------------------
Fruit | Orange
Fruit | Apple
Fruit | Pineapple
Fruit | Cherry
Vegetable | Potato
Vegetable | Celery
Vegetable | Ginger
Vegetable | Carrot
Drinks | Coffee
Drinks | Tea
Drinks | Coke
Drinks | Pepsi
I tried to use the following code, but it only displays 1 item per category instead of displaying all the items:
SELECT SubCategory, Item
FROM ItemList
GROUP BY SubCategory
ORDER BY item DESC
What I get is :
sub-category | item
-----------------------
Fruit | Apple
Vegetable | Carrot
Drinks | Coke
What I want is the following (example with a display limit of 2):
sub-category | item
-----------------------
Fruit | Apple
Fruit | Cherry
Vegetable | Carrot
Vegetable | Celery
Drinks | Coke
Drinks | Coffee
Well, most SQL implementations have a keyword: LIMIT <#> you can utilize at the end of your query, replacing # with the number of rows you want displayed. I don't know how you formatted your data, but if you drop that at the end of your query, it should suffice.
You cannot use the GROUP BY clause, because it will then only display one item per group. However, You can also use the ORDER BY clause, and this can be used for more than one item. For example...
SELECT * FROM table ORDER BY category DESC,subCategory DESC LIMIT 5;
If you only want to display the top 5 from each category though, you won't be able to do that with a single query.
* Someone have changed the original question. The question is totally not the same as before. So my answer would not be useful. *
I recommend you to add ID field to be a primary key and ID should be int.
Then you can use this code
select t1.*
from yourtable t1
where 5 >= (select count(t2.id)
from yourtable t2
where t1.category= t2.category
and t1.id >= t2.id
)
I hope this works on SQLite. I have tested it on MySQL.
In MSSQL you can partition by a column to and then use that to filter
SELECT
SubCategory,
Item
FROM
(
SELECT
SubCategory,
Item,
ROW_NUMBER() OVER(partition SubCategory, ORDER BY SubCategory) AS seq
FROM
ItemList
) AS t
WHERE seq < 3
http://msdn.microsoft.com/en-us/library/ms189461.aspx