SQL non alphabetical order in WHERE IN - sql

Let's say I have this query:
SELECT name
FROM product
WHERE name IN ('CE367FAACDHCANPH-151556',
'CE367FAACEX9ANPH-153877',
'NI564FAACJSFANPH-162605',
'GE526OTACCD3ANPH-149839')
the result is:
CE367FAACDHCANPH-151556
CE367FAACEX9ANPH-153877
GE526OTACCD3ANPH-149839
NI564FAACJSFANPH-162605
which is ordered by the alphabetical order
How can I get a result order by the index of appearance in the list?
basically I want this as a result:
CE367FAACDHCANPH-151556
CE367FAACEX9ANPH-153877
NI564FAACJSFANPH-162605
GE526OTACCD3ANPH-149839

This is quite a popular approach to sort things in SQL, so I've blogged about this example here. You would have to explicitly order by those values in your list, e.g. using a CASE expression:
SELECT name
FROM product
WHERE name IN ('CE367FAACDHCANPH-151556',
'CE367FAACEX9ANPH-153877',
'NI564FAACJSFANPH-162605',
'GE526OTACCD3ANPH-149839')
ORDER BY CASE WHEN name = 'CE367FAACDHCANPH-151556' THEN 1
WHEN name = 'CE367FAACEX9ANPH-153877' THEN 2
WHEN name = 'NI564FAACJSFANPH-162605' THEN 3
WHEN name = 'GE526OTACCD3ANPH-149839' THEN 4
END
Example on SQLFiddle
If you want to avoid repeating those literals, you could resort to this trick:
SELECT product.name
FROM product
JOIN (
VALUES('CE367FAACDHCANPH-151556', 1),
('CE367FAACEX9ANPH-153877', 2),
('NI564FAACJSFANPH-162605', 3),
('GE526OTACCD3ANPH-149839', 4)
) AS sort (name, sort)
ON product.name = sort.name
ORDER BY sort.sort
Example on SQLFiddle

You can use PATINDEX. At least if your names have more and less the same structure.
SELECT [Name] FROM product
ORDER BY CONVERT(INT, LEFT(Name, PATINDEX('%[^0-9]%', Name+'z')-1));
Check out this example on SQL Fiddle

Related

How to use LIKE with ORDER BY CASE for partial text search in WHEN statement?

How do you use LIKE with ORDER BY CASE in SQL? Or in other words, how do you do a partial text search for the following:
ORDER BY CASE [Column Name] WHEN [value partial text%]
Problem: I'm referencing a table (named "Personnel") with column (titled "Rank"), which lists each employee's job title followed by their level of certification (many variables). I would like to order the SQL query results by job title, ignoring the certification level that follows title name.
Example values in Personnel.Rank Column:
Captain Paramedic
Captain Intermediate
Captain EMT
Lieutenant Paramedic
Lieutenant Intermediate
Lieutenant EMT
Apparatus Operator Paramedic
Firefighter EMT
Firefighter AEMT
This works, but I don't want to list every variable as a WHEN clause:
SELECT
p.Rank
FROM Personnel p
ORDER BY
CASE p.Rank
WHEN 'Captain Paramedic' THEN 1
WHEN 'Captain EMT' THEN 1
WHEN 'Lieutenant Paramedic' THEN 2
WHEN 'Lieutenant EMT' THEN 2
ELSE 3
END
I would like to know how to do something like this instead:
SELECT
p.Rank
FROM Personnel p
ORDER BY
CASE p.Rank
WHEN LIKE 'Captain%' THEN 1
WHEN LIKE 'Lieutenant%' THEN 2
ELSE 3
END
Thoughts?
LIKE operator is not permitted with ORDER BY CASE [column name] WHEN statement
It is possible you can just fix up the syntax and still use your case statement. Untested but the syntax I would expect is:
SELECT
p.Rank
FROM Personnel p
ORDER BY
CASE
WHEN p.Rank LIKE 'Captain%' THEN 1
WHEN p.Rank LIKE 'Lieutenant%' THEN 2
ELSE 3
END
A more general purpose solution would be to use a reference or lookup table to get your order by values. Here is an example written in PSEUDOSQL just to show the idea. In real life, you can create your table or use a temp table or a CTE. The pro here is you can maintain this a little more cleanly (in your ref table - or SortOrder can be a field right in your Personnel table). The con here is you have "promoted" a simple ordering problem into something more permanent and if this is only an ad hoc need for an ad hoc query then it might be overkill.
create temporary table SortOrder (Rank, SortOrder)
insert into SortOrder
values
('Captain Paramedic', 10),
('Captain Intermediate', 10),
('Captain EMT', 10),
('Lieutenant Paramedic', 20),
('Lieutenant Intermediate', 20),
('Lieutenant EMT', 20),
('Apparatus Operator Paramedic', 30),
('Firefighter EMT', 40),
('Firefighter AEMT', 40)
SELECT
p.Rank
FROM
Personnel p
LEFT JOIN SortOrder s
ON p.Rank = s.Rank
ORDER BY
COALESCE(s.SortOrder, 100)

How can I group rows in SQL and sum them up?

Suppose I have a table like this in SQL Server 2017, let's call it "maps_and_cups"
some_code
quantity
big_map
6
tiny_map
5
big_cup
10
tiny_cup
4
I would like to know the best way to group the maps and cups into one, in this way.
some_code
quantity
maps
11
cups
14
I know that it is using "if" and "case", adding and comparing if it is a tiny_map, a big_map, and so on, I have seen several examples but I cannot make it compile.
You can indeed use a case when expression. For instance:
with base as
(select case some_code when 'big_map' then 'maps'
when 'tiny_map' then 'maps'
when 'big_cup' then 'cups'
when 'tiny_cup' then 'cups'
else 'other'
end grp,
quantity
from maps_and_cups)
select grp, sum(quantity) quantity from base group by grp;
However, if you're going to list each and every code explicitly, you might as well create a reference table for it:
some_code
grp
big_map
maps
tiny_map
maps
big_cup
cups
tiny_cup
cups
...and then join that table into your query:
select grp, sum(quantity)
from maps_and_cups a left join ref_maps_cups b on a.some_code = b.some_code
group by grp;
You can solve this task using "case" and "charindex" functions, like this:
declare
#t table (some_code varchar (20), quantity int)
insert into #t
values
('big_map', 6),
('tiny_map', 5),
('big_cup',10),
('tiny_cup', 4)
select
case
when charindex ('map', some_code)>0 then 'map'
when charindex ('cup', some_code)>0 then 'cup'
end some_code
,sum(quantity) quantity
from #t
group by
case
when charindex ('map', some_code)>0 then 'map'
when charindex ('cup', some_code)>0 then 'cup'
end
OUTPUT:
If you just want the right three characters for aggregating, you can use right():
select right(some_code, 3) + 's', sum(quantity)
from maps_and_cups
group by right(some_code, 3) + 's';
You are creating a problem for yourself as you're (probably) breaking the first normal form by storing non atomic values in the field "some_code". (Some field name i'd say. ;)
Why not separating the value into [ type ] and [ size ] ?

SQL Get count with most common part of string

I am able to get count of column with most same values, e.g.
SELECT COUNT(*) AS Count, ProjectID
FROM Projects
GROUP BY ProjectID
ORDER BY Count DESC
So now I have table like this,
ProjectID ProjectUrl
1 http://www.CompanyA.com/Projects/123
2 http://www.CompanyB.com/Projects/124
3 http://www.CompanyA.com/Projects/125
4 http://www.CompanyB.com/Projects/126
5 http://www.CompanyA.com/Projects/127
Now Expected result without providing any parameter
ProjectUrl = http://www.CompanyA.com Count = 3
ProjectUrl = http://www.CompanyB.com Count = 2
Edit
Sorry I forgot to mention types of Urls I have in the table, Urls are quiet random though, but there are urls that are common. As we are creating project categories, so project category url can be,
https://spanish.CompanyAa2342.com/portal/projectA/projectTeamA/ProjectPersonA/Task/124
but for some projects there are no project team or so on, so it's bit random :?
I will need to query something more like generic.
What Url will have in common
http://ramdomLanguage.CompanyName.com/portal/RandomName.....
Please try:
select
Col,
COUNT(Col) Cnt
from(
select
SUBSTRING(ProjectUrl, 0, PATINDEX('%.com/%', ProjectUrl)+4) Col
from tbl
)x group by Col
SQL Fiddle Demo
Not sure of performance when dealing with a huge dataset, but this is a solution. I've tried to get a row for each portion of the URLs, delimited by /. Then do a quick aggregate at the end to bring up the counts of each individual part. Fiddle is here: http://www.sqlfiddle.com/#!3/742c4/12 (I've added one row for demo's sake - thanks, TechDo.)
WITH cteFSPositions
AS
(
SELECT ProjectID,
ProjectURL,
1 AS CharPos,
MAX(LEN(ProjectURL)) AS MaxLen,
CHARINDEX('/', ProjectURL) AS FSPos
FROM Projects
GROUP BY ProjectID,
ProjectURL
UNION ALL
SELECT ProjectID,
ProjectURL,
CharPos + 1,
MaxLen,
CHARINDEX('/', ProjectURL, CharPos + 1) AS FSPos
FROM cteFSPositions
WHERE CharPos <= MaxLen
),
cteProjectURLParts
AS
(
SELECT DISTINCT ProjectID,
LEFT(ProjectURL, FSPos) AS ProjectURLPart,
FSPos
FROM cteFSPositions
WHERE FSPos > 0
UNION ALL
SELECT ProjectID,
ProjectURL,
LEN(ProjectURL)
FROM Projects
),
cteFilteredProjectURLParts
AS
(
SELECT ProjectID,
ProjectURLPart
FROM cteProjectURLParts
WHERE ProjectURLPart NOT IN ('http:', 'http:/', 'http://', 'https:', 'https:/', 'https://')
)
SELECT ProjectURLPart,
COUNT(*) AS Instances
FROM cteFilteredProjectURLParts
GROUP BY ProjectURLPart
ORDER BY Instances DESC,
ProjectURLPart;
This produces (with the additional row I added in):
ProjectURLPart Instances
http://www.CompanyA.com/ 4
http://www.CompanyA.com/Projects/ 3
http://www.CompanyB.com/ 2
http://www.CompanyB.com/Projects/ 2
http://www.CompanyA.com/BlahblahBlah/ 1
http://www.CompanyA.com/BlahblahBlah/More1/ 1
http://www.CompanyA.com/BlahblahBlah/More1/More2 1
http://www.CompanyA.com/Projects/123 1
http://www.CompanyA.com/Projects/125 1
http://www.CompanyA.com/Projects/127 1
http://www.CompanyB.com/Projects/124 1
http://www.CompanyB.com/Projects/126 1
EDIT: Oops, original post had code of fiddle in progress. Have supplied the finalized code and updated fiddle link.
EDIT 2: Realized I was cutting off the end part of the URLS due to the way I was cutting the URLs up. For completeness' sake, I've added them back in to the final dataset. Updated fiddle as well.

SQL query on a condition

I'm writing a query to retrieve translated content. I want it so that if there isn't a translation for the given language id, it automatically returns the translation for the default language, with Id 1.
select Translation.Title
,Translation.Summary
from Translation
where Translation.FkLanguageId = 3
-- If there is no LanguageId of 3, select the record with LanguageId of 1.
I'm working in MS SQL but I think the theory is not DBMS-specific.
Thanks in advance.
This assumes one row per Translation only, based on how you phrased the question. If you have multiple rows per FkLanguageId and I've misunderstood, please let us know and the query becomes more complex of course
select TOP 1
Translation.Title
,Translation.Summary
from
Translation
where
Translation.FkLanguageId IN (1, 3)
ORDER BY
FkLanguageId DESC
You'd use LIMIT in another RDBMS
Assuming the table contains different phrases grouped by PhraseId
WITH Trans As
(
select Translation.Title
,Translation.Summary
,ROW_NUMBER() OVER (PARTITION BY PhraseId ORDER BY FkLanguageId DESC) RN
from Translation
where Translation.FkLanguageId IN (1,3)
)
SELECT *
FROM Trans WHERE RN=1
This assumes the existance of a TranslationKey that associates one "topic" with several different translation languages:
SELECT
isnull(tX.Title, t1.Title) Title
,isnull(tX.Summary, t1.Summary) Summary
from Translation t1
left outer join Translation tX
on tx.TranslationKey = t1.Translationkey
and tx.FkLanguageId = #TargetLanguageId
where t1.FkLanguageId = 1 -- "Default
Maybe this is a dirty solution, but it can help you
if not exists(select t.Title ,t.Summary from Translation t where t.FkLanguageId = 3)
select t.Title ,t.Summary from Translation t where t.FkLanguageId = 1
else
select t.Title ,t.Summary from Translation t where t.FkLanguageId = 3
Since your reference to pastie.org shows that you're looking up phrases or specific menu item names in a table I'm going to assume that there is a phrase ID to identify the phrases in question.
SELECT ISNULL(forn_lang.Title, default_lang.Title) Title,
ISNULL(forn_lang.Summary, default_lang.Summary) Summary
FROM Translation default_lang
LEFT OUTER JOIN Translation forn_lang ON default_lang.PhraseID = forn_lang.PhraseID AND forn_lang.FkLanguageId = 3
WHERE default_lang.FkLanguageId = 1

SQL Sorting using Order by

Can you all please help me with this?
Presently, I have this SELECT which returns data ordered by this way
SELECT DISTINCT gl.group_id,
gl.group_name,
gl.group_description,
gl.status_code,
gl.member_count,
(
SELECT grpp.group_name
FROM test_group_relationship grel
JOIN test_group grpp
ON grel.parent_group_id = grpp.group_id
WHERE grel.child_group_id = gl.group_id
) AS parent_group_name,
gl.group_name_key,
gl.group_description_key
FROM test_group gl
WHERE gl.group_org_id = '3909'
AND gl.group_name_key like '%' || 'GROUP' || '%'
ORDER BY
gl.group_name_key, CONVERT(gl.group_name, 'WE8EBCDIC500')
The output is below.I have tried indenting the columns to paste the data.
GROUP_NAME GROUP_NAME_KEY
Add Group Basic Flow ADD GROUP BASIC FLOW
Administrative Group ADMINISTRATIVE GROUP
Amy Group 33 AMY GROUP 33
Amy Test Group 1 AMY TEST GROUP 1
another add group test from matt ANOTHER ADD GROUP TEST FROM MATT
**My Question is in the FIELD GROUP_NAME--> how can i SORT DATA using ORDER BY
so that lowercase letters will be sorted before uppercase letters.
Expected output is :-
the value "another add group test from matt" has to come at the first place.This way
lowercase letters are sorted first and then UPPER CASE.
See also:
SQL ORDER BY Issue Continued
PLSQL ORDER BY Issue
Convert the type on the field to a collation that is case sensitive and order by it asc
In your order by add
Group_Name COLLATE Latin1_General_CS_AS Asc
assuming that your characters are in english; otherwise substitute french etc.
try:
ORDER BY UPPER (SUBSTR (GROUP_NAME, 1, 1)), SUBSTR (GROUP_NAME, 1, 1) DESC, UPPER(GROUP_NAME), GROUP_NAME DESC;
Just add BINARY to your ORDER BY.
ORDER BY BINARY gl.group_name_key
You may have to use DESC otherwise upper case will come first. But then that would also sort z-a.
I'm not an Oracle guy, but depending on your version of Oracle, I believe that there are some session variables that will determine this for you. You can try the following:
ALTER SESSION SET nls_comp=binary;
ALTER SESSION SET nls_sort=GENERIC_M_CI;