Select Longest String - SQL Server - sql

I have a need to select the longest string in a SQL Server Query.
I need, the longest description from LongDescription_c when there is something in it or the longest from PartDescription.
At the moment, we have the following query created by a colleague a long time ago:
SELECT PartNum,
MAX(CASE WHEN LongDescription_c = '' THEN PartDescription ELSE LongDescription_c END) AS PartDesc
FROM
Part
GROUP BY
PartNum
This works in a lot of circumstances but not all and I believe the reason for this is the MAX(). As i believe this returns in alphabetical order rather then MAX(LEN()) which is what I believe he wanted. However, I cannot work out how to use MAX(LEN()) as this will purely return the maximum length as an integer.
Thanks for any help in advance as Im a bit stuck at this point.
Thanks :)

Along the lines of what #jarlh was saying, one option would be to use a TOP trick:
SELECT TOP 1 *
FROM Part
ORDER BY
LEN(CASE WHEN LongDescription_c = ''
THEN PartDescription ELSE LongDescription_c END) DESC,
CASE WHEN LongDescription_c = ''
THEN PartDescription ELSE LongDescription_c END
In the case of a tie, it would report the alphabetically lowest description first.
If performance be an issue, then adding an index is the typical thing to do. But in this case, because we are ordering using a function, the index cannot be used. If you really need performance, then consider adding a computed column to store the length.

Following query will give you row with longest LongDescription_c
SELECT TOP 1 * FROM [Part]
ORDER BY LEN(LongDescription_c) DESC

You would requited to use len() function in order to check length of string
select top(1) with ties PartNum, *
from table t
order by row_number() over (partition by PartNum order by
len(CASE WHEN LongDescription_c = ''
THEN PartDescription ELSE LongDescription_c END) desc)

I think your colleagues query is wrong.
If your LongDescription_c is always longer than PartDescription column, and only need to display PartDescription when LongDescription is not available, you can use a much simpler query
SELECT PartNum, IIF(LEN(LongDescription_c)>=LEN(PartDescription),LongDescription_c,PartDescription)
FROM Part

Related

SQL Query - how do I order part of my data in alphabetical order, and part by id

I have a small piece of data (15 records) where part of it I want ordered in alphabetical order, and part of it ordered by ID
Image 1 shows my data in the original order
After doing the query SELECT * FROM tableName ORDER BY code;
Image 2 shows my data now in alphabetical order, which is great however I would like the top 2 records to be ordered by id
Image 3 shows how I would like my data to look
Could someone help with my query please?
i assumed id is an integer. You can use conditional CASE in the ORDER BY clause.
Note for the first expression case when code in ('LUX-INT', 'LUX-CONT') then -id end desc, it will return id or NULL. As NULL will comes first in ORDER BY, I use DESC and negate the id value so that id is in ascending order
order by case when code in ('LUX-INT', 'LUX-CONT') then -id end desc, code
use case when
SELECT * FROM tableName ORDER BY case when code in ('LUX INT','LUX-CONT') then id else code end
I would write this as:
order by (case when code in ('LUX-INT', 'LUX-CONT') then 1 else 2 end), -- put the special codes first
(case when code in ('LUX-INT', 'LUX-CONT') then code end), -- order them alphabetically
id -- order the rest by id
This works regardless of the types and collations of the underlying columns.

SQL Group By and window function

I am struggling a little bit with an SQL Statement and was hoping if someone would be willing to point me in the right direction.
Below is a picture of my table:
As you can see here I have a column called 'State'. Basically I want to group all 'State' associated with a particular BuildID and display them in an extra column on my SQL output.
I am close but not quite there.
In the next picture below is an example of the statement I have used to try and achieve this:
As you can see here, it has done SUM and added the TotalTime and created the extra columns I require. However instead of grouping it all into one record, it has created 2 extra lines per BuildID. One with the value for the 'Running' state, another record for the value State of 'Break' and another record that contains the value 0 on both States.
Now what I want it to do is group it into one record for each BuildID. Like the picture I have added below:
The above image is how I want the records displayed. But the problem is I have added the WHERE [State] = Running, which I know is wrong but was just using this as an example. As you can see the 'Break' column has no value.
I hope this makes sense and was hoping if someone could point me in the right direction?
Here is an example on SQL fiddle http://sqlfiddle.com/#!3/7b6b9/2
Thanks for taking the time to read this :)
Have refined the sql a bit, here you go:
SELECT BuildID,Product, Program,
sum(CASE WHEN State = 'Running' THEN cast(TotalTime as INT) ELSE 0 END) AS Running
, sum(CASE WHEN State = 'Break' THEN cast(TotalTime as INT) ELSE 0 END) AS Breakt
FROM Line1Log
GROUP BY BuildID,Product, Program
Please check SQLFiddle
Not sure if i am missing something :-), but the solution looks pretty straight forward. Let me know.
move your SUM() OVER(PARTITION BY...) out from CASE
SQL fiddle
select BuildID, Product, Program, Sum(Running) Running, Sum([Break]) [Break]
from (
SELECT Distinct BuildID, Product, Program,
Sum(Case when [State]='Running' Then TotalTime Else 0 END) OVER (Partition by [State], [BuildID]) Running,
Sum(Case when [State]='Break' Then TotalTime Else 0 END) OVER (Partition by [State], [BuildID]) [Break]
From Line1Log) T
group by BuildID, Product, Program
Everything else is ok, why are you grouping all columns?, Try this,
Select BuidId,Product,program,Sum(Running),Sum(Break)
(......
.....
......) as T
group by BuidId,Product,program

Changing position of a row in sql

In the above t-sql table I would like very much for the Total row to appear at the bottom. I have been wracking my head against this and in all of my other Queries simply using ORDER BY Status works as Total is alphabetically much farther down the list than most of our row values.
This is not the case here and I just can't figure out how to change it
I'm pretty new to sql and I'be been having a lot of difficulty even determining how to phrase a google search. So far I've just gotten results pertaining to Order By
The results of a select query, unless an order is explicitly specified via an 'order by' clause, can be returned in any order. Moreover, the order in which they are returned is not even deterministic. Running the exact same query 3 times in succession might return the exact same result set in 3 different orderings.
So if you want a particular order to your table, you need to order it. An order by clause like
select *
from myTable t
where ...
order by case Status when 'Total' then 1 else 0 end ,
Status
would do you. The 'Total' row will float to the bottom, the other rows will be ordered in collating sequence. You can also order things arbitrarily with this technique:
select *
from myTable t
where ...
order by case Status
when 'Deceased' then 1
when 'Total' then 2
when 'Active' then 3
when 'Withdrawn' then 4
else 5
end
will list the row(s) with a status of 'Deceased' first, followed by the row(s) with a status of 'Total', then 'Active' and 'Withdrawn', and finally anything that didn't match up to an item in the list.
ORDER BY CASE WHEN STATUS = 'Total' THEN 'zzz' ELSE STATUS END
In SQL Server (and most other databases), you can use case to sort certain statūs above others:
order by
case Status
when 'Total' then 2
else 1
end
, Status
In MS Access, you can use iif:
order by
iif(Status = 'Total', 2, 1)
, Status
You can use conditional expressions in order by:
order by (case when status = 'Total' then 1 else 0 end),
status

designedly big value in SQL

Ok, I have to put null values last. The query should run under Oracle and MySQL.
I've already came up with
ORDER BY
CASE WHEN some_table.ord IS NULL THEN 9999999999 ELSE some_table.ord END
I should use value > max(some_table.ord) instead of 9999999999.
I think subquery to determine this value is too ugly here.
If this was C++ I can use some macro like INT_MAX for this purpose. Can you name its cross-DBMS SQL twin?
UPDATE
the question is if can I put something .. beautiful instead of 9999999999, so that query will work both in Oracle and MySQL,
not how to put null values last
Use an extra column for the null flag:
order by
case when some_table.ord is null then 2 else 1 end ,
some_table.ord
Or, if you have enough knowledge of the values that this column can take, just hard-code a number that is larger than anything in there:
order by coalesce(some_table.ord, 9999999999)
In Oracle, it's simply
ORDER BY some_table.ord NULLS LAST
Something like the following might work:
SELECT S.VAL1, S.VAL2, S.VAL3, COALESCE(S.ORD, O.MAX_ORD+1) AS ORD
FROM SOME_TABLE S,
(SELECT MAX(ORDER) AS MAX_ORD FROM SOME_TABLE) O
WHERE S.whatever = whichever AND
S.something <> something_else
ORDER BY ORD
Not sure if MySQL allows sub-queries in the FROM list. The idea here is to avoid the use of a magic value to handle the NULL case.
Share and enjoy.

Sql Server equivalent of a COUNTIF aggregate function

I'm building a query with a GROUP BY clause that needs the ability to count records based only on a certain condition (e.g. count only records where a certain column value is equal to 1).
SELECT UID,
COUNT(UID) AS TotalRecords,
SUM(ContractDollars) AS ContractDollars,
(COUNTIF(MyColumn, 1) / COUNT(UID) * 100) -- Get the average of all records that are 1
FROM dbo.AD_CurrentView
GROUP BY UID
HAVING SUM(ContractDollars) >= 500000
The COUNTIF() line obviously fails since there is no native SQL function called COUNTIF, but the idea here is to determine the percentage of all rows that have the value '1' for MyColumn.
Any thoughts on how to properly implement this in a MS SQL 2005 environment?
You could use a SUM (not COUNT!) combined with a CASE statement, like this:
SELECT SUM(CASE WHEN myColumn=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView
Note: in my own test NULLs were not an issue, though this can be environment dependent. You could handle nulls such as:
SELECT SUM(CASE WHEN ISNULL(myColumn,0)=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView
I usually do what Josh recommended, but brainstormed and tested a slightly hokey alternative that I felt like sharing.
You can take advantage of the fact that COUNT(ColumnName) doesn't count NULLs, and use something like this:
SELECT COUNT(NULLIF(0, myColumn))
FROM AD_CurrentView
NULLIF - returns NULL if the two passed in values are the same.
Advantage: Expresses your intent to COUNT rows instead of having the SUM() notation.
Disadvantage: Not as clear how it is working ("magic" is usually bad).
I would use this syntax. It achives the same as Josh and Chris's suggestions, but with the advantage it is ANSI complient and not tied to a particular database vendor.
select count(case when myColumn = 1 then 1 else null end)
from AD_CurrentView
How about
SELECT id, COUNT(IF status=42 THEN 1 ENDIF) AS cnt
FROM table
GROUP BY table
Shorter than CASE :)
Works because COUNT() doesn't count null values, and IF/CASE return null when condition is not met and there is no ELSE.
I think it's better than using SUM().
Adding on to Josh's answer,
SELECT COUNT(CASE WHEN myColumn=1 THEN AD_CurrentView.PrimaryKeyColumn ELSE NULL END)
FROM AD_CurrentView
Worked well for me (in SQL Server 2012) without changing the 'count' to a 'sum' and the same logic is portable to other 'conditional aggregates'. E.g., summing based on a condition:
SELECT SUM(CASE WHEN myColumn=1 THEN AD_CurrentView.NumberColumn ELSE 0 END)
FROM AD_CurrentView
It's 2022 and latest SQL Server still doesn't have COUNTIF (along with regex!). Here's what I use:
-- Count if MyColumn = 42
SELECT SUM(IIF(MyColumn = 42, 1, 0))
FROM MyTable
IIF is a shortcut for CASE WHEN MyColumn = 42 THEN 1 ELSE 0 END.
Not product-specific, but the SQL standard provides
SELECT COUNT() FILTER WHERE <condition-1>,
COUNT() FILTER WHERE <condition-2>, ...
FROM ...
for this purpose. Or something that closely resembles it, I don't know off the top of my hat.
And of course vendors will prefer to stick with their proprietary solutions.
Why not like this?
SELECT count(1)
FROM AD_CurrentView
WHERE myColumn=1
I had to use COUNTIF() in my case as part of my SELECT columns AND to mimic a % of the number of times each item appeared in my results.
So I used this...
SELECT COL1, COL2, ... ETC
(1 / SELECT a.vcount
FROM (SELECT vm2.visit_id, count(*) AS vcount
FROM dbo.visitmanifests AS vm2
WHERE vm2.inactive = 0 AND vm2.visit_id = vm.Visit_ID
GROUP BY vm2.visit_id) AS a)) AS [No of Visits],
COL xyz
FROM etc etc
Of course you will need to format the result according to your display requirements.
SELECT COALESCE(IF(myColumn = 1,COUNT(DISTINCT NumberColumn),NULL),0) column1,
COALESCE(CASE WHEN myColumn = 1 THEN COUNT(DISTINCT NumberColumn) ELSE NULL END,0) AS column2
FROM AD_CurrentView