Return one result for each search value - sql

How can I return a unique value based on a set of non unique values being searched?
For example:
If I wanted to return one phone number for a list of 4 people who can have more than one phone number - but I can only use one phone number for each person. It doesn't matter which phone number I use to reach them because any number that belongs to them will get me to them.
I don't think something like this exists - but if I could use something like the DISTINCT modifier except it would be called FIRST - it would solve my problem:
SELECT FIRST ID
FROM Sample_Table
WHERE ID in ("Bob", "Sam", "Kyle", "Jordan")
In picture - from this
I'd like that (or any) query to return
.
I'm using this type of query in a db where for 200 "ID"s there are millions of "Unique Values", so it is hard to get crafty.
EDIT The Unique value in my db has numbers and letters in each value

There is no such thing as a "first id" in a SQL table. Tables represent unordered sets. You can accomplish what you want (if I understand correctly) using aggregation:
SELECT ID, MIN(UniqueValue)
FROM Sample_Table
WHERE ID in ('Bob', 'Sam', 'Kyle', 'Jordan')
GROUP BY ID;

using group by and max method can help you:
select id
,uniquvalue
,max (typeofvalue)
from Sample_Table
group by
id
,uniquvalue

Related

Count only a specific subset of elements in a Postgres DB

I have a table with some identifiers that repeat themselves like
id
-------
djkfgh
kdfjhw
efkujh
dfsggs
djkfgh
djkfgh
efkujh
I also have a list of id's of interest, say ["djkfgh","dfsggs"]. I would like to count only those values that appear in the list, rather than all the distinct values of the column.
Select count(id) from table where id IN(subset);

SQL Random String from List

I want to choose from a list of strings and assign that as the value of one of the columns for my SELECT.
Something like the following:
SELECT id, name, GET_RANDOM_TYPE('Basic', 'Silver', 'Gold', 'Premium') AS type
FROM tbl
I'm just doing some tests hence why I need this.
Not terribly familiar with oracle, but perhaps you can simply use round(dbms_random.value(1,4)) in conjunction with a CASE expression:
SELECT id,
CASE round(dbms_random.value(1,4))
WHEN 1 THEN 'Basic'
WHEN 2 THEN 'Silver'
WHEN 3 THEN 'Gold'
WHEN 4 THEN 'Premium'
END AS type
FROM table
Create a table with your list of values that has a number as a primary key.
Then
Select your_text
from your_random_table
where ID = TRUNC(DBMS_RANDOM.value(1,10));
The statement above will give you any one 10 pseudo random numbers and assumes you have 10 random values in your table. It's not really random but works for testing. See here.

SQL select id=1

I've a table that has id_categoria field having comma separated value, e.g., 1,2,3,4,64,31,12,14, because a record can belong to multiple categories. If I want to select records that belongs to category 1, I have to run following SQL query
SELECT *
FROM cme_notizie
WHERE id_categoria LIKE '1%'
ORDER BY `id` ASC
and then select all records from the record set that have id_categoria exactly 1 in id_categoria. Let's assume that the value 1 does not exist, but column value like 12, 15, 120 ... still contains 1.
There is a way to take only 1? without taking derivatives or other?
As comments say, you probably shouldn't do that. Instead, you should have another table with one row per category. But if you decide to go with this inferior solution, you can do the following:
SELECT *
FROM cme_notizie
WHERE CONCAT(',', id_categoria, ',') LIKE '%,1,%'
ORDER BY id ASC

How do I check if all posts from a joined table has the same value in a column?

I'm building a BI report for a client where there is a 1-n related join involved.
The joined table has a field for employee ID (EmplId).
The query that I've built for this report is supposed to give a 1 in its field "OneEmployee" if all the related posts have the same employee in the EmplId field, null if it's different employees, i.e:
TaskTrans
TaskTransHours > EmplId: 'John'
TaskTransHours > EmplId: 'John'
This should give a 1 in the said field in the query
TaskTrans
TaskTransHours > EmplId: 'John'
TaskTransHours > EmplId: 'George'
This should leave the said field blank
The idea is to create a field where a case function checks this and returns the correct value. But my problem is whereas there is a way to check for this through SQL.
select not count(*) from your_table
where employee_id = GIVEN_ID
and your_field not in ( select min(your_field)
from your_table
where employee_id = GIVEN_ID);
Note: my first idea was to use LIMIT 1 in the inner query, but MYSQL didn't like it, so min it was - the points to use any, but only one. Min should work, but the field should be indexed, then this query will actually execute rather fast, as only indexes would be used (obviously employee_id should also be indexed).
Note2: Do not get too confused with not in front of count(*), you want 1 when there is none that is different, I count different ones, and then give you the not count(*), which will be one if count is 0, otherwise 0.
Seems a job for a window COUNT():
SELECT
…,
CASE COUNT(DISTINCT TaskTransHours.EmplId) OVER () WHEN 1 THEN 1 END
AS OneEmployee
FROM …

How do I select unique records by column with ActiveRecord and Postgresql

Given the following records (the first row being the column names):
name platform other_columns date
Eric Ruby something somedate
Eric Objective-C something somedate
Joe Ruby something somedate
How do I retrieve a singular record with all columns, such that the name column is always unique in the results set? I would like the query in this example to return the first Eric (w/ Ruby) record.
I think the closest I've gotten is to use "select distinct on (name) *...", but that requires me to order by name first, when I actually want to order the records by the date column.
Order records by date
If there are multiple records with the same name, select one (which does not matter)
Select all columns
How do I achieve this in Rails on PostgreSQL?
You can't do a simple .group(:name) because that produces a GROUP BY name in your SQL when you'll be selecting ungrouped and unaggregated columns, that leaves ambiguity as to which row to pick and PostgreSQL (rightly IMHO) complains:
When GROUP BY is present, it is not valid for the SELECT list expressions to refer to ungrouped columns except within aggregate functions, since there would be more than one possible value to return for an ungrouped column.
If you start adding more columns to your grouping with something like this:
T.group(T.columns.collect(&:name))
then you'll be grouping by things you don't want to and you'll end up pulling out the whole table and that's not what you want. If you try aggregating to avoid the grouping problem, you'll end up mixing different rows (i.e. one column will come from one row while another column will come from some other row) and that's not what you want either.
ActiveRecord really isn't built for this sort of thing but you can bend it to your will with some effort.
You're using AR so you presumably have an id column. If you have PostgreSQL 8.4 or higher, then you could use window functions as a sort of localized GROUP BY; you'll need to window twice: once to figure out the name/thedate pairs and again to pick just one id (just in case you have multiple rows with the same name and thedate which match the earliest thedate) and hence get a unique row:
select your_table.*
from your_table
where id in (
-- You don't need DISTINCT here as the IN will take care of collapsing duplicates.
select min(yt.id) over (partition by yt.name)
from (
select distinct name, min(thedate) over (partition by name) as thedate
from your_table
) as dt
join your_table as yt
on yt.name = dt.name and yt.thedate = dt.thedate
)
Then wrap that in a find_by_sql and you have your objects.
If you're using Heroku with a shared database (or some other environment without 8.4 or higher), then you're stuck with PostgreSQL 8.3 and you won't have window functions. In that case, you'd probably want to filter out the duplicates in Ruby-land:
with_dups = YourTable.find_by_sql(%Q{
select yt.*
from your_table yt
join (select name, min(thedate) as thedate from your_table group by name) as dt
on yt.name = dt.name and yt.thedate = dt.thedate
});
# Clear out the duplicates, sorting by id ensures consistent results
unique_matches = with_dups.sort_by(&:id).group_by(&:name).map { |x| x.last.first }
If you're pretty sure that there won't be duplicate name/min(thedate) pairs then the 8.3-compatible solution might be your best bet; but, if there will be a lot of duplicates, then you want the database to do as much work as possible to avoid creating thousands of AR objects that you're just going to throw away.
Maybe someone else with stronger PostgreSQL-Fu than me will come along and offer something nicer.
I you don't care for which row is retrieved when multiple names are there (this will be true for all columns) and the table has that structure you can simply do a query like
SELECT * FROM table_name GROUP BY `name` ORDER BY `date`
or in Rails
TableClass.group(:name).order(:date)
Get a list of names and minimum dates, and join that back to the original table to get the rowset you're looking for.
select
b.*
from
(select name, min(date) as mindate from table group by name) a
inner join table b
on a.name = b.name and a.mindate = b.date
I know this question is 8 years old. Current ruby version is 2.5.3. 2.6.1 is released. Rails stable version is 5.2.2. 6.0.0 beta2 is released.
Lets name your table Person.
Person.all.order(:date).group_by(&:name).map{|p| p.last.last}
Person.all.order(:date).group_by(&:name).collect {|key, value| value.last}
Explanation: First get all records in person table. Then sorted by date (descending or ascending) and then group by name (record with duplicate name will be grouped).
Person.all.order(:date).group_by(&:name)
This returns hash.
{"Eric" => [#<Person id: 1, name: "Eric", other_fields: "">, #<Person id: 2, name: "Eric", other_fields: "">], "Joe" => [#<Person id: 3, name: "Joe", other_fields: "">]}
Solution 1: .map method.
Person.all.order(:date).group_by(&:name).map{|p| p.last.last}
We got hash. We loop that as array. p.last will give
[[#<Person id: 1, name: "Eric", other_fields: "">, #<Person id: 2, name: "Eric", other_fields: "">],[#<Person id: 3, name: "Joe", other_fields: "">]]
Get first or last record of nested array using p.last.first or p.last.last.
Solution 2: .collect or .each method.
Person.all.order(:date).group_by(&:name).collect {|key, value| value.last}