DBIx::Class : Resultset order_by based upon existence of a value in the list - sql

I am using DBIx::Class and I have got a ResultSet. I like to re-order the ResultSet. I like to check a particular column "City" against a fix list of values ("London", "New York" "Tokyo") If city is found in the list of values I like to move that result to the top group. If city is not found, I like to move that result to the bottom group in the ResultSet.

ORDER BY expr might be what you're looking for.
For example, here a table:
mysql> select * from test;
+----+-----------+
| id | name |
+----+-----------+
| 1 | London |
| 2 | Paris |
| 3 | Tokio |
| 4 | Rome |
| 5 | Amsterdam |
+----+-----------+
Here the special ordering:
mysql> select * from test order by name = 'London' desc,
name = 'Paris' desc,
name = 'Amsterdam' desc;
+----+-----------+
| id | name |
+----+-----------+
| 1 | London |
| 2 | Paris |
| 5 | Amsterdam |
| 3 | Tokio |
| 4 | Rome |
+----+-----------+
Translating this into a ResultSet method:
$schema->resultset('Test')->search(
{},
{order_by => {-desc => q[name in ('London', 'New York', 'Tokyo')] }}
);

Something like:
#!/usr/bin/env perl
use strict;
use warnings;
my $what = shift or die;
my #ary = qw(alpha beta gamma);
unshift(#ary,$what) unless ( grep(/$what/,#ary) );
print "#ary\n";
1;
Run as:
./myscript omega

Related

How do I get values that are themselves not unique, but are linked to unique fields(in 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
)

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 query multiple rows into a single-line string result?

Let's say we have this table named phrases and it has contents like so:
phrases
+----+--------+
| id | phrase |
+----+--------+
| 1 | the |
| 2 | quick |
| .. | ... |
| 8 | lazy |
| 9 | dog |
+----+--------+
Desired result
+---------------------------------------------+
| sentence |
+---------------------------------------------+
| the quick brown fox jumps over the lazy dog |
+---------------------------------------------+
What should be the query statement such that it would result into a single result string like the above?
You can use STRING_AGG with an empty delimiter, and probably with a sort order
SELECT STRING_AGG(phrase , '' ORDER BY id) as sentence
FROM phrases;

Fetch SQL rows with priority

I have following tables structure:
forms
RID | MODULE
------------
1 | indiv
2 | indiv
3 | indiv
translations
RID | LANG | VALUE | MODULE | TAG |
-----------------------------------
1 | en |car | | |
1 | en |truck |indiv | |
1 | en |boat |indiv |C100 |
2 | en |hat | | |
3 | en |cat | | |
3 | en |dog |indiv | |
4 | en |light | | |
5 | en |dark | | |
I need to fetch only one row per RID from translations table, based on additional (but not mandatory) parameters for module and tag columns, i.e.:
RESULT without input parameters:
RID | LANG | VALUE | MODULE | TAG |
-----------------------------------
1 | en |car | | |
2 | en |hat | | |
3 | en |cat | | |
RESULT with one input parameter module='indiv':
RID | LANG | VALUE | MODULE | TAG |
-----------------------------------
1 | en |truck |indiv | |
2 | en |hat | | |
3 | en |dog |indiv | |
If I have two input parameters the result to be:
RESULT with two parameters: module='indiv' AND tag='c100'
RID | LANG | VALUE | MODULE | TAG |
-----------------------------------
1 | en |boat |indiv |C100 |
2 | en |hat | | |
3 | en |dog |indiv | |
How can I achieve this with SQL only on ORACLE DB server? A query example for the last case with two parameters will be enough for me as previous cases are subsets from last one with NULL of these columns I believe. If you think that all these cases are too different and require different SQL statements, you are more than welcome to write them as well.
Thank you!
SELECT *
FROM (
SELECT t.*,
ROW_NUMBER() OVER (
PARTITION BY RID
ORDER BY CASE
WHEN module = LOWER( :mod ) AND tag = UPPER( :tag ) THEN 1
WHEN tag = UPPER( :tag ) THEN 2
WHEN module = LOWER( :mod ) AND tag IS NULL THEN 3
WHEN module IS NULL AND tag IS NULL THEN 4
ELSE 5
END
) AS rn
FROM translations t
WHERE ( module IS NULL OR module = LOWER( :mod ) )
OR ( tag IS NULL OR tag = UPPER( :tag ) )
)
WHERE rn = 1;
I think this should do.
SELECT *
FROM (
SELECT F.RID,
T.LANG,
T.VALUE,
T.MODULE,
T.TAG,
RANK() OVER(PARTITION BY F.RID
ORDER BY DECODE(T.MODULE, :module, 1, 2),
DECODE(T.TAG, :tag, 1, 2)) RANK
FROM forms F INNER JOIN translations T
ON T.RID = F.RID)
WHERE RANK = 1
So you rank rows with MODULE = :module or/and TAG = :tag higher. You still need something to do with ties, but you get the idea. RANK leaves ties, ROW_NUMBER does not.
I put MODULE higher than TAG because of your examples. You might need to change it if you can input tags without modules.
And also, DECODE maps NULL to NULL, so if :module is not set, you will get match with rows having NULL in MODULE.

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.