How do I get values that are themselves not unique, but are linked to unique fields(in SQL)? - sql

I can't give the actual table, but my problem is something like this:
Assuming that there is a table called Names with entries like these:
+--------------+
| name | id |
+--------------+
| Jack | 1001 |
| Jack | 1022 |
| John | 1010 |
| Boris | 1092 |
+--------------+
I need to select all the unique names from that table, and display them(only names, not ids). But if I do:
SELECT DISTINCT name FROM Names;
Then it will return:
+-------+
| name |
+-------+
| Jack |
| John |
| Boris |
+-------+
But as you can see in the table, the 2 people named "Jack" are different, since they have different ids. How do I get an output like this one:
+-------+
| name |
+-------+
| Jack |
| Jack |
| John |
| Boris |
+-------+
?
Assuming that some ids can or will be repeated(not marked primary key in question)
Also, in the question, the result will have 1 column and some number of rows(exact number is given, its 18,013). Is there a way to check if I have the right number of rows? I know I can use COUNT(), but while selecting the unique values I used GROUP BY, so using COUNT() would return the counts for how many names have that unique id, as in:
SELECT name FROM Names GROUP BY id;
+------------------+
| COUNT(name) | id |
+------------------+
| 2 | 1001 |
| 1 | 1022 |
| 1 | 1092 |
| 3 | 1003 |
+------------------+
So, is there something to help me verify my output?

You can use group by:
select name
from names
group by name, id;

You can get all the distinct persons with:
SELECT DISTINCT name, id
FROM names
and you can select from the above query only the names:
SELECT name
FROM (
SELECT DISTINCT name, id
FROM names
)

Related

How can I load rows by name and role and order them based on their name, but preference the name? (SQL)

What I mean is the following.
I have a database containing people. These people van a name and a role.
It looks like this:
-----------------------------
| (PK) id | name | role |
-----------------------------
| 1 | ben | fireman |
| 2 | ron | cook |
| 3 | chris | coach |
| 4 | remco | barber |
-----------------------------
Ive created a searchbar where you can search for people in the database. When you press search, it looks for name and roles, for example:
When I type in 'co', the result I get is:
-----------------------------
| (PK) id | name | role |
-----------------------------
| 3 | chris | coach |
| 4 | remco | barber |
| 2 | ron | cook |
-----------------------------
This is because its looking for matches in the name and role column.
The query I use is:
SELECT * FROM people WHERE name LIKE '$search' OR role LIKE '$search' ORDER BY name";
The only issue with this is that it just order by name.
I want it to first order every result from the name column by name and then order every remaining result from the role column by name, so it ends up looking like this:
-----------------------------
| (PK) id | name | role |
-----------------------------
| 4 | remco | barber | <- 'co' found in name column, ordered by name
| 3 | chris | coach | <- 'co' found in role column, ordered by name
| 2 | ron | cook | <- 'co' found in role column, ordered by name
-----------------------------
How can I do this?
Edit: $search is the output from the searchbar
Use a case expression to put the 'co' names first:
order by case when name LIKE '$search' then 0 else 1 end, name, role

How to select distinct values from a table?

I am working with SQL Server database and I have a table called companiesData with three columns: id, name, projects I want to get the values from the id and name without the repeated values.
Content of the tables:
| id | name | project |
| 1 | Company A | Project A |
| 2 | Company B | Project A |
| 3 | Company B | Project B |
| 4 | Company A | Project B |
If I write:
select distinct name from companiesData;
The query returns:
| name |
| Company A |
| Company B |
But like I said in the beginning I want the data from two columns of the same table (I only have one right now). So I write the next query to get the data:
select distinct id, company from companiesData;
And it returns:
| id | name |
| 1 | Company A |
| 2 | Company B |
| 3 | Company B |
| 4 | Company A |
So, I also tried with:
select id, company from companiesData group by id, company;
But this returns the same data of the second query. In others questions the answer to this problem is use distinct or group by but this is not working for me.
Is there any other way to get this data? Am I wrong in my queries?
Thanks in advance.
So you want "Company A" to appear once in your results, with one of the ids, and you don't care which one? How about:
select min(id), company from companiesData group by company

SELECTing Related Rows Based on a Single Row Match

I have the following table running on Postgres SQL 9.5:
+---+------------+-------------+
|ID | trans_id | message |
+---+------------+-------------+
| 1 | 1234567 | abc123-ef |
| 2 | 1234567 | def234-gh |
| 3 | 1234567 | ghi567-ij |
| 4 | 8902345 | ced123-ef |
| 5 | 8902345 | def234-bz |
| 6 | 8902345 | ghi567-ij |
| 7 | 6789012 | abc123-ab |
| 8 | 6789012 | def234-cd |
| 9 | 6789012 | ghi567-ef |
|10 | 4567890 | abc123-ab |
|11 | 4567890 | gex890-aj |
|12 | 4567890 | ghi567-ef |
+---+------------+-------------+
I am looking for the rows for each trans_id based on a LIKE query, like this:
SELECT * FROM table
WHERE message LIKE '%def-234%'
This, of course, returns just three rows, the three that match my pattern in the message column. What I am looking for, instead, is all the rows matching that trans_id in groups of messages that match. That is, if a single row matches the pattern, get all the rows with the trans_id of that matching row.
That is, the results would be:
+---+------------+-------------+
|ID | trans_id | message |
+---+------------+-------------+
| 1 | 1234567 | abc123-ef |
| 2 | 1234567 | def234-gh |
| 3 | 1234567 | ghi567-ij |
| 4 | 8902345 | ced123-ef |
| 5 | 8902345 | def234-bz |
| 6 | 8902345 | ghi567-ij |
| 7 | 6789012 | abc123-ab |
| 8 | 6789012 | def234-cd |
| 9 | 6789012 | ghi567-ef |
+---+------------+-------------+
Notice rows 10, 11, and 12 were not SELECTed because there was not one of them that matched the %def-234% pattern.
I have tried (and failed) to write a sub-query to get the all the related rows when a single message matches a pattern:
SELECT sub.*
FROM (
SELECT DISTINCT trans_id FROM table WHERE message LIKE '%def-234%'
) sub
WHERE table.trans_id = sub.trans_id
I could easily do this with two queries, but the first query to get a list of matching trans_ids to include in a WHERE trans_id IN (<huge list of trans_ids>) clause would be very large, and would not be a very inefficient way of doing this, and I believe there exists a way to do it with a single query.
Thank you!
This will do the job I think :
WITH sub AS (
SELECT trans_id
FROM table
WHERE message LIKE '%def-234%'
)
SELECT *
FROM table JOIN sub USING (trans_id);
Hope this help.
Try this:
SELECT ID, trans_id, message
FROM (
SELECT ID, trans_id, message,
COUNT(*) FILTER (WHERE message LIKE '%def234%')
OVER (PARTITION BY trans_id) AS pattern_cnt
FROM mytable) AS t
WHERE pattern_cnt >= 1
Using a FILTER clause in the windowed version of COUNT function we can get the number of records matching the predefined pattern within each trans_id slice. The outer query uses this count to filter out irrelevant slices.
Demo here
You can do this.
WITH trans
AS
(SELECT DISTINCT trans_id
FROM t1
WHERE message LIKE '%def234%')
SELECT t1.*
FROM t1,
trans
WHERE t1.trans_id = trans.trans_id;
I think this will perform better. If you have enough data, you can do an explain on both Sub query and CTE and compare the output.

PostgreSQL integer array value join to integer in other table with desc string

I have a table test column with int arrays and values like {1000,4000,6000} or {1000} or {1000,4000} called ekw.
These values match to a description string in another table
tab: test
id | name | ekw
-----------------
1 | One | {1000}
2 | Two | {1000,4000}
3 | Three | {1000,4000,6000}
tab: ekwdesc
id | value | desc
-----------------
1 | 1000 | Max
2 | 2000 | Tim
3 | 3000 | Rita
5 | 4000 | Sven
6 | 5000 | Tom
7 | 6000 | Bob
is it possible to select these columns and print the strings?
something like:
select name, ekw from test, ekwdesc
I would like to see this result:
id | name | ekwdesc
-----------------
1 | One | Max
2 | Two | Max, Sven
3 | Three | Max, Sven, Bob
I tried with IN and ANY but couldn't get it to work.
You had the right idea to use the any operator for the join. Once the join is complete, all that's left is to use string_agg to transform the result to the format you want:
SELECT name, STRING_AGG(description, ', ')
FROM test
JOIN ekwdesc ON ekwdesc.value = ANY(test.ekw)
GROUP BY name
See the attached SQLFiddle for an executable example.

SQL for calculated column that chooses from value in own row

I have a table in which several indentifiers of a person may be stored. In this table I would like to create a single calculated identifier column that stores the best identifier for that record depending on what identifiers are available.
For example (some fictional sample data) ....
Table = "Citizens"
Id | LastName | DL-No | SS-No | State-Id-No | Calculated
------------------------------------------------------------------------
1 | Smith | NULL | 374-784-8888 | 7383204848 | ?
2 | Jones | JG892435262 | NULL | NULL | ?
3 | Trask | TSK73948379 | NULL | 9276542119 | ?
4 | Clinton | CL231429888 | 543-123-5555 | 1840430324 | ?
I know the order in which I would like choose identifiers ...
Drivers-License-No
Social-Security-No
State-Id-No
So I would like the calculated identifier column to be part of the table schema. The desired results would be ...
Id | LastName | DL-No | SS-No | State-Id-No | Calculated
------------------------------------------------------------------------
1 | Smith | NULL | 374-784-8888 | 7383204848 | 374-784-8888
2 | Jones | JG892435262 | NULL | 4537409273 | JG892435262
3 | Trask | NULL | NULL | 9276542119 | 9276542119
4 | Clinton | CL231429888 | 543-123-5555 | 1840430324 | CL231429888
IS this possible? If so what SQL would I use to calculate what goes in the "Calculated" column?
I was thinking of something like ..
SELECT
CASE
WHEN ([DL-No] is NOT NULL) THEN [DL-No]
WHEN ([SS-No] is NOT NULL) THEN [SS-No]
WHEN ([State-Id-No] is NOT NULL) THEN [State-Id-No]
AS "Calculated"
END
FROM Citizens
The easiest solution is to use coalesce():
select c.*,
coalesce([DL-No], [SS-No], [State-ID-No]) as calculated
from citizens c
However, I think your case statement will also work, if you fix the syntax to use when rather than where.