Issue regarding order by CHARINDEX Sql Server - sql

This is my whole script
CREATE TABLE #TEST (
STATE CHAR(2))
INSERT INTO #TEST
SELECT 'ME' UNION ALL
SELECT 'ME' UNION ALL
SELECT 'ME' UNION ALL
SELECT 'SC' UNION ALL
SELECT 'NY' UNION ALL
SELECT 'SC' UNION ALL
SELECT 'NY' UNION ALL
SELECT 'SC'
SELECT *
FROM #TEST
ORDER BY CHARINDEX(STATE,'SC,NY')
I want to display all records start with SC first and second with NY and then rest come without any order.
When I execute the above sql then first all records come with ME which is not in my order by list.
Tell me where I am making the mistake. Thanks.

Use CASE statement
SELECT *
FROM #TEST
ORDER BY CASE WHEN STATE LIKE 'SC%' THEN 0
WHEN STATE LIKE 'NY%' THEN 1
ELSE 2
END
Output
STATE
-----
SC
SC
SC
NY
NY
ME
ME
ME

See here for what CHARINDEX returns when your search pattern is Not found.
- https://msdn.microsoft.com/en-GB/library/ms186323.aspx
To avoid dealing with the 0 case, you could use the below.
ORDER BY
CASE LEFT(state, 2)
WHEN 'SC' THEN 1
WHEN 'NY' THEN 2
ELSE 3
END
This should be easier to write, read and maintain.
It should also use less CPU, though I doubt it will make a tangible difference.

CASE/IIF with LIKE is one way to do it . You can also alter your CHARINDEX like this to get what you want.The Problem in your case is that in ascending order, no match records are shown first as 0 is returned for such records. You can Reverse for criteria to 'NY,SC' and do a DESC order.
Query
SELECT *
FROM #TEST
ORDER BY CHARINDEX(STATE,'NY,SC') DESC,State
Output
SC
SC
SC
NY
NY
ME
ME
ME

Related

How to parametrize SQL query, by passing multiple value

I have a SQL query, in where clause "MID = 123". I want to use a parameter for the VALUE and pass multiple values.
Example: now I am passing one VALUE of 123, but I want to pass multiple value like 123, 124, 125 etc. from which the SQL query will take values one by one and pass those to the where clause, and produce the result one by one, like first use value = 123, get the result, when use value = 124 and get results, and last use value = 125 to get results.
I need help with this parameter.
It will be great if we don't change the SQL query but we can parameter the where clause.
NOTE: I am using SQL Developer.
SQL query:
SELECT
'ABC' AS COLUMN_NAME,
(CASE
WHEN to_char(count(ABC)) > 1
AND to_char(max(ABC)) = to_char(min(ABC))
AND to_char(count(ABC)) = count(*)
AND to_char(max(ABC)) IS NULL
THEN 'same'
ELSE 'Diff'
END) AS COMPARISON_VALUE,
(CASE
WHEN to_char(COUNT(ABC)) = 1 OR to_char(min(ABC)) IS NULL
THEN 'No Values'
ELSE to_char(max(ABC))
END) AS TRANSACTION1,
to_char(min(ABC)) AS TRANSACTION2
FROM
ADVICES
WHERE
MID = '123';
One approach would be to specify the test data in a WITH clause and feed it to the query via an outer join. We include the test value as the first column in the result set. This means that every row in the result set will have the associated test value.
WITH testdata(testval) AS
(
SELECT '123' FROM DUAL UNION ALL
SELECT '124' FROM DUAL UNION ALL
SELECT '125' FROM DUAL
)
SELECT
testdata.testval,
...
...
FROM
ADVICES, testdata
WHERE
MID (+) = testdata.testval;
...i am trying to parameter the where clause, for which i want to fetch
parameter data from text or CSV should be fine.
Alternatively, testdata could be a table that's loaded from a CSV file with test values.
You may pass a CSV and use LIKE.
where ','||:var||',' like '%,'||MID||',%'
Example using HR schema
var myvar VARCHAR2
exec :myvar := '101,102,103' --parameter
select employee_id,department_id from employees where
','||:myvar||',' like '%,'||employee_id||',%';
Result
EMPLOYEE_ID DEPARTMENT_ID
----------- -------------
101 90
102 90
103 60
You can write your WHERE clause like the following:
WHERE MID IN
(SELECT
REGEXP_SUBSTR('YOUR_VALUE', '[^,]+', 1, LEVEL)
FROM
DUAL
CONNECT BY
REGEXP_SUBSTR('YOUR_VALUE', '[^,]+', 1, LEVEL) IS NOT NULL);
For checking the solution:
-- With multiple values
DEFINE YOUR_VALUE= '123,124,125';
SELECT
REGEXP_SUBSTR('&YOUR_VALUE', '[^,]+', 1, LEVEL)
FROM
DUAL
CONNECT BY
REGEXP_SUBSTR('&YOUR_VALUE', '[^,]+', 1, LEVEL) IS NOT NULL);
-- OUTPUT --
REGEXP_SUBS
-----------
123
124
125
-- With single value
DEFINE YOUR_VALUE = '123';
SELECT
REGEXP_SUBSTR('&YOUR_VALUE', '[^,]+', 1, LEVEL)
FROM
DUAL
CONNECT BY
REGEXP_SUBSTR('&YOUR_VALUE', '[^,]+', 1, LEVEL) IS NOT NULL;
-- OUTPUT --
REGEXP_SUBS
-----------
123
Cheers!!
You can change your where clause to -
WHERE
MID IN (YOUR_PARAMETER);
When you will pass multiple values, it will simply converted to OR condition and will fetch the results.

Excluding a row that contains a specific value

I want to exclude people who have joined a specific group. For example, if some students signed up for an Orchestra club, and I want to retrieve a list of students who did NOT sign up for orchestra, how do I do so?
I am unable to simply do a Group By clause because some students may have joined multiple clubs, and would bypass the Where condition and still show up in the query,
as shown here.
I am thinking about using a CASE statement in the SELECT clause to flag the person as '1' if they have joined Orchestra, and '0' if they have not, but I'm struggling to write an aggregate CASE function, which would cause issues from the GROUP BY clause.
Any thoughts on how to flag people with a certain row value?
Apparently my table didn't get saved onto SQLFiddle so you can paste the code below on your own screen:
CREATE TABLE activity ( PersonId, Club) as
select 1, 'Soccer' from dual union
select 1, 'Orchestra' from dual union
select 2, 'Soccer' from dual union
select 2, 'Chess' from dual union
select 2, 'Bball' from dual union
select 3, 'Orchestra' from dual union
select 3, 'Chess' from dual union
select 3, 'Bball' from dual union
select 4, 'Soccer' from dual union
select 4, 'Bball' from dual union
select 4, 'Chess' from dual;
Use the HAVING clause instead of using WHERE, with case expression :
HAVING max(case when column = ‘string’ then 1 else 0 end) = 0
Add this after your group by .
How about selecting a list of user ids from the activity table and excluding it:
SELECT * FROM users WHERE id NOT IN
(SELECT PersonId FROM activity WHERE Club = 'Orchestra');
You could use a subquery to return a list of people to exclude.
-- Returns person 2 and 4.
SELECT
PersonId
FROM
activity
WHERE
PersonId NOT IN
(
-- People to exclude.
SELECT
PersonId
FROM
activity
WHERE
Club = 'Orchestra'
)
GROUP BY
PersonId
;
EDIT Removed superfluous distinct in subquery - thanks #mathguy.
select * from
(
select a.*, case when Club ='Orchestra' then 1 else 0 end flag
from activity a
) where flag =1; --> get some students signed up for an Orchestra club
select * from
(
select a.*, case when Club ='Orchestra' then 1 else 0 end flag
from activity a
) where flag =0; --> get students not signed up for an Orchestra club

sql find max value of a column and select as new field

I am currently working with some ambulance data and in some scenario's multiple vehicles are dispatched to the same call. Of those dispatched sometimes they are stood down(diverted to a different call.)
The relevent fields are incidentID, VehicleAllocationSequenceNumber snd VehicleArrivalAtSceneDateTime.
An incidentID can map to multiple rows with different VehicleAllocationSequenceNumber and VehicleArrivalAtSceneDateTime.
VehicleAllocationSequenceNumber defines the order in which the vehicles arrived on the scene. VehicleArrivalAtSceneDateTime is either a datetime or NULL if the vehicle never arrived.
I want to select distinct IncidentID's with and condense the other columns into two new features; Number_of_Vehicles_assigned and number_of_vehicles_on_scene
I am fairly new to queries of this complexity and I am looking for a way to do this without using a cursor.
Thanks in advance
Edit:
Nevermind I nailed the query, think I'm just a bit scatter brained this morning. Here is my query for posterity's sake.
Feedback is welcome:
select
IncidentID,
max(VehicleAllocationSequenceNumber) as number_vehicles_assigned,
count(VehicleArrivalAtSceneDateTime) as number_of_vehicles_on_scene
FROM
[dwuserobj].[acc].[AmbulanceLinkedDatasetForModelling]
where
IncidentID in(
select distinct
IncidentID
from
[dwuserobj].[acc].[AmbulanceLinkedDatasetForModelling]
)
and vehicleArrivalAtSceneDateTime is not null
group by
IncidentID;
Use a case with a not exists
select distinct incidentID,
case
when not exists
(
select 1
from AmbulanceTable t2
where t2.incidentID = t1.incidentID
and t2.VehicleAllocationSequenceNumber is null
) then 1
else 0 as alloc,
case
when not exists
(
select 1
from AmbulanceTable t3
where t3.incidentID = t1.incidentID
and t3.VehicleArrivalAtSceneDateTime is null
) then 1
else 0 as onscene
from AmbulanceTable t1
Edit:
So no_vehicles is number of vehicles... my bad:
select incidentid
count(VehicleAllocationSequenceNumber) as num_assigned,
count(VehicleArrivalAtSceneDateTime ) as num_oncscene
from AmbulanceLinkedDatasetForModelling
group by incidentid
this will give you zeros if none assigned/on scene
this should provide you with the logic you need. I made a temp table so you can see how the incidentID interacts with the AllocationSequenceNumber and the SceneDateTime.
DECLARE #AmbulanceLinkedDatasetForModelling TABLE (IncidentID int, VehicleAllocationSequenceNumber int, VehicleArrivalAtSceneDateTime datetime)
INSERT INTO #AmbulanceLinkedDatasetForModelling
SELECT 1, 500, NULL UNION ALL -- the vehicle never arrived
SELECT 2, 501, '2016-05-20 06:07:00.370' UNION ALL
SELECT 3, 502, '2018-01-05 08:15:08.970' UNION ALL
SELECT 4, 503, NULL UNION ALL -- the vehicle never arrived
SELECT 4, 504, '2017-04-11 11:22:01.360'UNION ALL
SELECT 4, 505, '2017-04-11 11:32:01.360'
SELECT
IncidentID,
number_vehicles_assigned=COUNT(DISTINCT VehicleAllocationSequenceNumber),
no_vehicles_on_scene=SUM(CASE WHEN VehicleArrivalAtSceneDateTime IS NOT NULL THEN 1 ELSE 0 END)
FROM
#AmbulanceLinkedDatasetForModelling
GROUP BY
IncidentID

sql group by ignoring case and suffix or final letter

I have a table like this:
I am going to count the number of categories and how many rows are in each category.
I used this query:
But unfortunately apples is counted as separate category because it has "s" at the end.
I would recommend you look at some of the comments and restructure your data as I think it will cause you issues going forward but this query will do what you want but it isnt a nice one.
CTE for testing:
WITH fruit_table(Fruit, No_Fruit)
AS (
SELECT 'Apple', 3
UNION ALL
SELECT 'Apples', 2
UNION ALL
SELECT 'Orange', 1
UNION ALL
SELECT 'oranges', 2)
Query:
SELECT DISTINCT
LOWER(CASE
WHEN right(fruit, 1) = 's'
THEN left(fruit, -1)
ELSE fruit
END),
SUM(No_Fruit)
FROM fruit_table
GROUP BY LOWER(CASE
WHEN right(fruit, 1) = 's'
THEN left(fruit, -1)
ELSE Fruit
END);
There will be a more elegant way to get the results you want and a better solution would be to fix your schema but.... it works

MS SQL does not return the expected top row when ordering by DIFFERENCE()

I have noticed strange behaviour in some SQL code used for address matching at the company I work for & have created some test SQL to illustrate the issue.
; WITH Temp (Id, Diff) AS (
SELECT 9218, 0
UNION
SELECT 9219, 0
UNION
SELECT 9220, 0
)
SELECT TOP 1 * FROM Temp ORDER BY Diff DESC
Returns 9218 but
; WITH Temp (Id, Name) AS (
SELECT 9218, 'Sonnedal'
UNION
SELECT 9219, 'Lammermoor'
UNION
SELECT 9220, 'Honeydew'
)
SELECT TOP 1 *, DIFFERENCE(Name, '') FROM Temp ORDER BY DIFFERENCE(Name, '') DESC
returns 9219 even though the Difference() is 0 for all records as you can see here:
; WITH Temp (Id, Name) AS (
SELECT 9218, 'Sonnedal'
UNION
SELECT 9219, 'Lammermoor'
UNION
SELECT 9220, 'Honeydew'
)
SELECT *, DIFFERENCE(Name, '') FROM Temp ORDER BY DIFFERENCE(Name, '') DESC
which returns
9218 Sonnedal 0
9219 Lammermoor 0
9220 Honeydew 0
Does anyone know why this happens? I am writing C# to replace existing SQL & need to return the same results so I can test that my code produces the same results. But I can't see why the actual SQL used returns 9219 rather than 9218 & it doesn't seem to make sense. It seems it's down to the Difference() function but it returns 0 for all the record in question.
When you call:
SELECT TOP 1 *, DIFFERENCE(Name, '')
FROM Temp l
ORDER BY DIFFERENCE(Name, '') DESC
All three records have a DIFFERENCE value of zero, and hence SQL Server is free to choose from any of the three records for ordering. That is to say, there is no guarantee which order you will get. The same is true for your second query. Actually, it is possible that the ordering for the same query could even change over time. In practice, if you expect a certain ordering, you should provide exact logic for it, e.g.
SELECT TOP 1 *
FROM Temp
ORDER BY Id;