Sybase SQL Select Distinct Based on Multiple Columns with an ID - sql

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

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.

SQL SELECT Sort by specific row

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.

Return column name and distinct values

Say I have a simple table in postgres as the following:
+--------+--------+----------+
| Car | Pet | Name |
+--------+--------+----------+
| BMW | Dog | Sam |
| Honda | Cat | Mary |
| Toyota | Dog | Sam |
| ... | ... | ... |
I would like to run a sql query that could return the column name in the first column and unique values in the second column. For example:
+--------+--------+
| Col | Vals |
+--------+--------+
| Car | BMW |
| Car | Toyota |
| Car | Honda |
| Pet | Dog |
| Pet | Cat |
| Name | Sam |
| Name | Mary |
| ... | ... |
I found a bit of code that can be used to return all of the unique values from multiple fields into one column:
-- Query 4b. (104 ms, 128 ms)
select distinct unnest( array_agg(a)||
array_agg(b)||
array_agg(c)||
array_agg(d) )
from t ;
But I don't understand the code well enough to know how to append the column name into another column.
I also found a query that can return the column names in a table. Maybe a sub-query of this in combination with the "Query 4b" shown above?
SQL Fiddle
SELECT distinct
unnest(array['car', 'pet', 'name']) AS col,
unnest(array[car, pet, name]) AS vals
FROM t
order by col
It's bad style to put set-returning functions in the SELECT list and not allowed in the SQL standard. Postgres supports it for historical reasons, but since LATERAL was introduced Postgres 9.3, it's largely obsolete. We can use it here as well:
SELECT x.col, x.val
FROM tbl, LATERAL (VALUES ('car', car)
, ('pet', pet)
, ('name', name)) x(col, val)
GROUP BY 1, 2
ORDER BY 1, 2;
You'll find this LATERAL (VALUES ...) technique discussed under the very same question on dba.SE you already found yourself. Just don't stop reading at the first answer.
Up until Postgres 9.4 there was still an exception: "parallel unnest" required to combine multiple set-returning functions in the SELECT list. Postgres 9.4 brought a new variant of unnest() to remove that necessity, too. More:
Unnest multiple arrays in parallel
The new function is also does not derail into a Cartesian product if the number of returned rows should not be exactly the same for all set-returning functions in the SELECT list, which is (was) a very odd behavior. The new syntax variant should be preferred over the now outdated one:
SELECT DISTINCT x.*
FROM tbl t, unnest('{car, pet, name}'::text[]
, ARRAY[t.car, t.pet, t.name]) AS x(col, val)
ORDER BY 1, 2;
Also shorter and faster than two unnest() calls in parallel.
Returns:
col | val
------+--------
car | BMW
car | Honda
car | Toyota
name | Mary
name | Sam
pet | Cat
pet | Dog
DISTINCT or GROUP BY, either is good for the task.
With JSON functions
row_to_json() and json_each_text() you can do it not specifying number and names of columns:
select distinct key as col, value as vals
from (
select row_to_json(t) r
from a_table t
) t,
json_each_text(r)
order by 1, 2;
SqlFiddle.

Search from comma separated values in column

I have a table with 2 columns - id and pets.
Pets column contain abbreviated pet names separated by , [comma] as shown below
+----+-------------+
| id | pets |
+----+-------------+
| 1 | CAT,DOG |
+----+-------------+
| 2 | CAT,DOG,TIG |
+----+-------------+
| 3 | ZEB,MOU |
+----+-------------+
Now I want to list all id's where pets = CAT, similarly all id's where pets = DOG etc
My initial try was to RUN the following SQL for each and every PET (CAT, DOG, etc)
select id
from "favpets"
where pets like '%CAT%'
The limitation of this simple solution is that the actual table and no. of pets are not as simple as mentioned above.
No. of such pets are more than 200. Therefore, 200 sql's have to be executed in order to list all id's corresponding to the pets
Is there any good alternative solution ? I'm using doctrine, so does doctrine provide any good implementation ?
With this query you will obtain all id, all pets, ordered by pet:
SELECT id, unnest(string_to_array(pets, ',')) AS mypet
FROM favpets
ORDER BY mypet;
Using it as subquery it will became easy to group and count:
SELECT mypet, COUNT(*) FROM
(
SELECT id, unnest(string_to_array(pets, ',')) AS mypet
FROM favpets
ORDER BY mypet
) AS a
GROUP BY mypet;

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)
);