Alphanumeric Sorting SSRS - reporting-services

I have a report that creates a parts list from my MFG software. The part number list currently looks like this:
DA100-12
DA100-121
DA100-122
DA100-13
DA100-131
I want them to sort taking into account the numeric part at the end like this:
DA100-12
DA100-13
DA100-121
DA100-122
DA100-131
Does anyone have suggestions on how to accomplish this within a report?

You could split at the - and then sort by the last part numerically. One solution would be to do this in the underlying database query already, maybe like this:
SELECT PartNo, SUBSTR(PartNo, 1, CHARINDEX('-', PartNo)) AS PN, CONVERT(INT, SUBSTR(PartNo, CHARINDEX('-', PartNo) + 1, LEN(PartNo))) AS NumPart
You may need to tweek the SUBSTR indices a bit, I can not actually try this right now. Then have SSRS sort by PN and NumPart. The result of the query should look something like this:
PartNo PN NumPart
---------------------------
DA100-12 DA100 12
DA100-121 DA100 121
DA100-122 DA100 122
DA100-13 DA100 13
DA100-131 DA100 131
I'm pretty sure that you can also do the splitting in SSRS itself, too, in the sort expression.

You can also leverage the T-SQL function PARSENAME for this, if there are no '.' characters in your data and if there is always exactly one '-' separating the parts. (Ultimately, you may be better off redesigning things so the two parts of the part name are in separate columns.)
select * from Parts
order by
PARSENAME(REPLACE(PartName,'-','.'),2),
CAST(PARSENAME(REPLACE(PartName,'-','.'),1) AS INT)
Click here for a SQL Fiddle repro.

Related

Sum columns with similar names in SQL

Relatively new to SQL and want to shorten a query I’m using.
The goal is to add the total spent in one year and compare it to the next year. However, the column names are all formatted “Spend_YYYYMM” so “Spend_202102.”
Currently, my solution is just to add all 12 columns up:
SELECT
“Full_Name”,
(“Spend_202001”+”Spend_202002”...) AS “2020 Total”,
(“Spend_201901”+”Spend_201902”...) AS “2019 Total”
FROM “Customers”
WHERE “2019 Total” > “2020 Total”;
So is there a way to look for columns where it starts with “Spend_2019” and add them up without having to type all 12 columns out? Or is what I have the only way we can really do this?
(Sorry for all the superfluous quotes, it’s apparently how our DB works with SQL.)
Thank you for your help!!
First, do not use identifiers that need to be escaped.
Second, your data model is weak. You should have separate rows for the different years.
But, the answer to your question is a MySQL extension of the HAVING clause:
SELECT Full_Name,
(Spend_202001 + Spend_202002 ...) AS Total_2020,
(Spend_201901 + Spend_201902 ...) AS Total_2019
FROM Customers
HAVING Total_2019 > Total_2020 ;

Show Fields but with content

i'm trying to show fields names on a combobox, but I need only those that are not null or have blank spaces.
I all ready have the field names with this query:
SHOW FIELDS FROM model WHERE type LIKE 'varchar(15)'
Any idea about how can i do this?
Update:
I'm working with an old database who is poorly designed. I attached an image:Database Screenshot This is a tire sizes database, so i need to get the years by model who has the size captured to show them in a combo box.
You can use your current query to get the "candidate" fields, but (short of some very complicated dynamic sql) you'll need to build a separate query to determine which candidates are pertinent. Generically, something like:
SELECT SUM(IF(field1 REGEXP '[a-zA-Z0-9.]+', 1, 0) > 0 AS showField1
, SUM(IF(field2 REGEXP '[a-zA-Z0-9.]+', 1, 0) > 0 AS showField2
, ...
FROM theTable
;
Depending on what you consider "has values" the regexp string may need adjusted; learn more here: http://dev.mysql.com/doc/refman/5.7/en/regexp.html
If the table is huge (large number of rows), you may be better off querying on each field separately like this (the one above will be looking at the whole table without some sort of WHERE to reduce the rows examined):
SELECT 1 FROM theTable WHERE fieldX REGEXP '[a-zA-Z0-9.]+' LIMIT 1;
Treating having a query result as "yes" and no result as "no content".

Extracting a substring matched in Regex and bounded by spaces

Trying to parse dates entered in various ways and contexts, and that may or may not be present in a given record
I can SELECT candidate rows with
SELECT * FROM table WHERE column REGEXP '[-|.|/][0-9][0-9][-|.|/]' ;
This will indeed select records that read something like
I was on top of mount Everest (2010-10-10)
i went to see the doctor on 13/12/10 and she told me I was in great shape.
where the matched values are -10- and /12/ for the first and second records respectively.
Now, I want to extract the date from the column. Not merely the -10- or /12/ but the full date fragments 2010-10-10 or 13/12/10, i.e. the matched expression expanded backwards up to a space or a parenthesis, and expanded forward at as space of parenthesis.
Sorry if this is obvious - I am not familiar with REGEX.
you will have to find a pattern for the date input. you can use regex in your where, but you will need to isolate it somehow. is it always the last part of the col?
now that you isolated the location, you can do a case style select
select case
when right(date,4) between 1900 and 2200 then right(date,10) #mm/dd/yyyy
when left(date,4) between 1900 and 2200 then concantenate(left(right(date,5),2), "/", right(date,2))
end as date
that kind of ordeal
EDIT;;
SET #fieldName = "I was ON top of mount Everest (2010-10-22)";
SELECT IF(
STR_TO_DATE(
CONCAT (
RIGHT(SUBSTRING_INDEX(#fieldName,"-",1),4), "-",RIGHT(SUBSTRING_INDEX(#fieldName,"-",2),2), "-",LEFT(SUBSTRING_INDEX(#fieldName,"-",-1),2)
), '%Y-%m-%d'
) IS NULL ,
"bad date",
"good date")
but now for bad date and good date, you keep chaining that style to loop through all variants of dates...
although the best solution is to make that date a diff col in a special format if you can as it is entered
The proper REGEX (in this case) is [0-9+-]+[-|.|/][0-9][0-9][-|.|/]+[0-9+-]+
Your pattern [0-9+-]+[-./][0-9][0-9][-./]+[0-9+-]+ would match stuff like +-+-.99///.///-++++, is that really what you want?
Consider using
(?:(?P<year>\d{3,4})|(\d{1,2}))(?P<sep>[-./])\d{1,2}(?P=sep)(?(year)\d{1,2}|\d{1,4})
instead. It doesn't allow mixed separators like 1.2-2014, and doesn't allow more than one number to have more than 2 digits like 2010-10-2010.
Demo.

Converting a upper case database to proper case

I am new to SQL and I have several large database with upper case first and last names that I need to convert to proper case in SQL sever 2008.
I am using the following to do this:
update database
Set FirstNames = upper(substring(FirstNames, 1, 1))
+ lower(substring(FirstNames, 2, (len(FirstNames) - 1) ))
I was wondering if there was any way to adapt this so that a field with two first names is also updated (currently I make the change and then go through and manually change the second name).
I have looked over the other answers in this field and they all seem quit long, compared to the query above.
Also is there any way to assist with converting the Mc suranmes ( I will manually change the others)? MCDONALD to McDonald, again I am just using the about query but replacing the FirstNames with LastName.
This is probably best done outside of SQL. However, if there is a requirement to do it on the server or if speed isn't an issue (because it will be an issue so you need to figure out if you care), the way you are going about it is probably the best way of doing so. If you want, you could create a UDF that puts all of the logic in one area.
Here is some code I came across (with attribution and more information below it):
CREATE FUNCTION dbo.fCapFirst(#input NVARCHAR(4000)) RETURNS NVARCHAR(4000)
AS
BEGIN
DECLARE #position INT
WHILE IsNull(#position,Len(#input)) > 1
SELECT #input = Stuff(#input,IsNull(#position,1),1,upper(substring(#input,IsNull(#position,1),1))),
#position = charindex(' ',#input,IsNull(#position,1)) + 1
RETURN (#input)
END
--Call it like so
select dbo.fCapFirst(Lower(Column)) From MyTable
I got this code from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=37760 There is more information and other suggestions in this forum as well.
As for dealing with cases like the McDonald, I would suggest one of two ways to handle this. One would be to put a search in the above UDF for key names ('McDonald', 'McGrew', etc.) or for patterns (the first two letters are Mc then make the next one capital, etc.) The second way would be to put these cases (the full names) in a table and have their replacement value in a second column. Then simply do a replace. Most likely, however, it will be easiest to identify rules like Mc then capitalize instead of trying to list every last-name possibility.
Don't forget you may want to modify the above UDF to include dashes, not just spaces.
Maybe this is too long but it is very easy and can be adapted for -, ', etc:
UPDATE tbl SET LastName = Case when (CharIndex(' ',lastname,1)<>0) then (Upper(Substring(lastname,1,1))+Lower(Substring(lastname,2,CharIndex(' ',lastname,1)-1)))+
(Upper(Substring(lastname,CharIndex(' ',lastname,1)+1,1))+
Lower(Substring(lastname,CharIndex(' ',lastname,1)+2,Len(lastname)-(CharIndex(' ',lastname,1)-1))))
else (Upper(Substring(lastname,1,1))+Lower(Substring(lastname,2,Len(lastname)-1))) end,
FirstName = Case when (CharIndex(' ',firstname,1)<>0) then (Upper(Substring(firstname,1,1))+Lower(Substring(firstname,2,CharIndex(' ',firstname,1)-1)))+
(Upper(Substring(firstname,CharIndex(' ',firstname,1)+1,1))+
Lower(Substring(firstname,CharIndex(' ',firstname,1)+2,Len(firstname)-(CharIndex(' ',firstname,1)-1))))
else (Upper(Substring(firstname,1,1))+Lower(Substring(firstname,2,Len(firstname)-1))) end;
Tony Rogerson has code that deals with:
double barrelled names eg Arthur Bentley-Smythe
Control characters
I haven't used it myself though...

Sorting MySQL results for diversity

I have a table called Classes which stores information on College classes. It has the columns Department, Course, and Section. I currently have an autocomplete where users can enter classes. For example if they type "ECON" it shows ECON-E 201 12345, etc. The autocomplete query is LIMIT'ed to 10.
Now, the problem with my autocomplete is that it doesn't give diversity in its responses. If they type "E" it will show 10 ECON classes, but not ENG (English) classes. Is there a way to sort the response to give as many departments, courses, and sections (in that order) as possible?
It depends also on the setup of your database. If you have a separate field for the names (such as "ECON"), then you could do:
SELECT DISTINCT course FROM classes WHERE UPPER(course) LIKE 'E'
Looking at your comment, how does this work for you? (I'm only checking against Department_Code, but in place of that, you could have your REPLACE line...)
SELECT Class_ID, Department_Code,
Course_Code, Class_Code FROM Classes
GROUP BY CASE WHEN (SELECT
COUNT(DISTINCT Department_Code)=1 FROM
Classes WHERE Department_Code LIKE '%"
. $q ."%') THEN Class_ID ELSE
Department_Code END HAVING
Department_Code LIKE '%" . $q ."%' LIMIT 10
This will return only a single record for each of the Departments that begin with 'E', but if the user enters 'ENG', then all of the courses for 'ENG' will be pulled.
There is a way to do this although it depends on where you want to do it at. For example do you want MySQL to do the delimiting or do you want to push this to the server side scripting to delimit/diversify your data?
Personally I see as a possibility, querying the database one or two times with different queries to diversify your data. Such as "SELECT * FROM classes WHERE couse like e" and then potentially take the first result such as "ECON..." and on next query do perhaps "SELECT * FROM classes WHERE !(course like "ECON")"
If you want server side scripting diversification it will require querying a much larger set of data instead of the 10. Lets say 50. From this you could return 0,1,2,3,4,20,30,40,49,50 of the result set. This might not be most intelligent way and it also requires you to pull a larger data set from database.
Original SELECT:
SELECT Class_ID, Department_Code, Course_Code, Class_Code, FROM Classes WHERE REPLACE(REPLACE(REPLACE(CONCAT(Department_Code, Course_Code, Class_Code), '-', ''), '(', ''), ')', '') LIKE '%". $q ."%' LIMIT 10
Diversify Query: (I will use to replace the above original to keep it shorter)
<originalQuery> UNION SELECT * FROM Classes WHERE SUBSTRING(<originalQuery>,1,1) LIKE '%".$q."%'
This might not be exactly it, but basic idea is you want your original result set (left side of the union) along with the second query where I take the result and call a substring on it so in your example probably "E" is like the user query. Hopefully this would give make a mixed set, I feel its not quite right but it should give you an idea of what you can do.