Microsoft SQL Server max of character and numeric values in same column - sql-server

I am taking the max of a column that contains both numeric and varchar values (i.e. '2008', 'n/a'). What is considered the max? The string or numeric value?
I am working in Microsoft SQL Server.
Thanks!

The Numeric value is actually a string.
MAX finds the highest value in the collating sequence
For collating sequence of ASCII chars refer below link.
https://www.ibm.com/support/knowledgecenter/SSQ2R2_9.5.1/com.ibm.ent.cbl.zos.doc/PGandLR/ref/rlebcasc.html

For character columns, MAX finds the highest value in the collating sequence.
- max() docs
It will be the same value as if you order by col desc
Here are some values thrown into a column and sorted descending:
+------------+
| col |
+------------+
| Z |
| na |
| n/a/ |
| 9999999999 |
| 30 |
| 2008 |
| 00000000 |
+------------+
The max() would be the first value from the above. Z.
rextester demo: http://rextester.com/IXXX76837
The exact order will depend on your the collation of your column (most default to Latin1_General_CI_AS).
Here is a demo that shows you the sort order for each character for some different collations (latin general / latin binary)
rextester demo: http://rextester.com/WLJ38844

create table one
(
Col1 varchar(200)
)
insert into one(Col1)
values('2008'),('n/a'),('aaaa'),('bbb'),('zzzz')
select max(Col1) from one
--zzzz

n/a would be max
It is better to eliminate the known 'n/a' values and cast them to some thing meaningful while applying the max or min functions.
select max(cast(column as int))
from tablename
where column != 'n/a'

Related

SQL Server Padding 0's with RIGHT Not Working

I am using SQL Server 2008.
I have a varchar(30) column which holds check numbers. If the payment was made using a credit card, checknbr will be a zero-length string. I need to pad this data with leading zeroes such that the entire output is ten digits long. If there is no check number, it should just be ten zeroes.
Here is the data I would like to see:
0000000000
0000000000
0000000114
0000000105
0000000007
Here is the query I have been using and am stuck on:
SELECT RIGHT((CASE
WHEN LEN(checknbr) = 0 THEN '0000000000'
ELSE '0000000000'+checknbr
END),10) as checknbr
FROM payhistory
WHERE number = 12345678
And this is what I get:
0000000000
0000000000
114
105
7
Trying another way:
SELECT RIGHT('0000000000'+checknbr,10)
FROM payhistory
WHERE number = 3861821
And I get the same results. Oddly enough, I have a varchar(10) column where
RIGHT('0000'+EDC.DatabaseNumber,4)
yields exactly the results I am after. What am I missing here? Something about a quirk with the RIGHT function?
Thanks in advance
If you are using SQL server 2012 or up try the following:
SELECT RIGHT(CONCAT('0000000000', checknbr), 10)
Concat will automatically convert NULL values to empty strings, removing the need for extra checks.
By the way, the issue you're having is the fact that your query still sees checknbr as a number instead of a varchar value. Casting it to varchar will solve that for you.
EDIT SQL Server 2008:
Since you have a VARCHAR column with no NULL values the only thing that still comes to mind are trailing spaces. Try:
SELECT RIGHT('0000000000' + RTRIM(checknbr), 10);
using cast() or convert() to convert checknbr to a character value will solve the implicit conversion of '0000000000' to the integer 0.
select right('0000000000'+convert(varchar(10),checknbr),10)
from payhistory
where number = 3861821
If checknbr can be null and you want it to return as '0000000000', then you can wrap it in coalesce() or isnull():
select right('0000000000'+isnull(convert(varchar(10),checknbr),''),10)
from payhistory
where number = 3861821
If checknbr is not a number, but varchar(30), then (as Jens points out), you may have padded spaces that need to be trimmed:
rextester demo: http://rextester.com/IRLV83801
create table payhistory (checknbr varchar(30));
insert into payhistory values ('0'), ('1'), (' 0'), ('0 '),(' ');
select checknbr = right('0000000000'+checknbr,10)
from payhistory
returns:
+------------+
| checknbr |
+------------+
| 0000000000 |
| 0000000001 |
| 0 |
| 0 |
| |
+------------+
And if you trim the padded spaces:
select checknbr = right('0000000000'+ltrim(rtrim(checknbr)),10)
from payhistory
returns:
+------------+
| checknbr |
+------------+
| 0000000000 |
| 0000000001 |
| 0000000000 |
| 0000000000 |
| 0000000000 |
+------------+

Compare different rows of a mdx query between them

I need some help. I'm trying to compare different rows of a mdx query between them.
For example, imagine I have 2 different columns (Col1 and Col2 which are entities and they mean something like Col1 has to pay Col2) and I have 1 measure (the amount that Col1 have to pay for Col2). My query gets a result like the following.
Col1 | Col2 | Result
A | B | 20
A | C | 30
B | C | 10
B | A | -20
C | A | -30
C | B | -10
I'm not able to check for example if the amount from A to B and the amount from B to A are the same.
Can someone give me an advice ?
Thanks a lot
I think you can use a SSRS expression to determine the Result value to be paid by Col2 to Col1.
Create a new column in your tablix using this expression:
=Lookup(Fields!Col2.Value & Fields!Col1.Value,
Fields!Col1.Value & Fields!Col2.Value,Fields!Result.Value,"DataSetName")
Replace DataSetName by the actual name of yours.
It will give you the corresponding Result value:
If there is no a row for a specific Col2 - Col1 relation it will produce Nothing value.
Let me know if this helps.

TSQL - how to avoid truncating or rounding

For some reason, the data in the ORIGINAL_BOOK column (even though it has 2 decimals (eg. 876.76)), the statement below truncates the decimals. I want the decimals to be visible as well. Can someone suggest how to fix this issue please?
Case
When [DN].[SETTLEMENT_DATE] > [DN].[AS_OF_DATE]
Then Cast([DN].[ORIGINAL_BOOK] as decimal(28, 2))
Else Cast([DN].[CURRENT_BOOK] as decimal(28, 2))
End
I can't be sure because you only say that the type of the fields involved is NUMERIC, without specifying any precision or scale, however if your source fields really are just NUMERIC type, SQL defaults to NUMERIC(18,0) (as per MSDN documentation here) and so you will only be able to store values with a scale of zero (i.e. no value after the decimal place) and any values written to these fields with a greater scale (i.e. data after the decimal place) will be rounded accordingly:
CREATE TABLE dn (
ORIGINAL_BOOK NUMERIC,
CURRENT_BOOK NUMERIC
)
INSERT INTO dn
SELECT 876.76, 423.75
UNION
SELECT 0, 0
UNION
SELECT 1.1, 6.5
UNION
SELECT 12, 54
UNION
SELECT 5.789, 6.321
SELECT CAST(dn.ORIGINAL_BOOK AS DECIMAL(28,2)) AS ORIGINAL_BOOK_CONV,
CAST(dn.CURRENT_BOOK AS DECIMAL(28,2)) AS CURRENT_BOOK_CONV
FROM dn
DROP TABLE dn
gives results:
/----------------------------------------\
| ORIGINAL_BOOK_CONV | CURRENT_BOOK_CONV |
|--------------------+-------------------|
| 0.00 | 0.00 |
| 1.00 | 7.00 |
| 6.00 | 6.00 |
| 12.00 | 54.00 |
| 877.00 | 424.00 |
\----------------------------------------/
Increasing the scale of the field in the table will allow values with greater numbers of decimal places to be stored and your CAST call will then reduce the number of decimal places if appropriate:
CREATE TABLE dn (
ORIGINAL_BOOK NUMERIC(28,3),
CURRENT_BOOK NUMERIC(28,3)
)
INSERT INTO dn
SELECT 876.76, 423.75
UNION
SELECT 0, 0
UNION
SELECT 1.1, 6.5
UNION
SELECT 12, 54
UNION
SELECT 5.789, 6.321
SELECT CAST(dn.ORIGINAL_BOOK AS DECIMAL(28,2)) AS ORIGINAL_BOOK_CONV,
CAST(dn.CURRENT_BOOK AS DECIMAL(28,2)) AS CURRENT_BOOK_CONV
FROM dn
DROP TABLE dn
gives results:
/----------------------------------------\
| ORIGINAL_BOOK_CONV | CURRENT_BOOK_CONV |
|--------------------+-------------------|
| 0.00 | 0.00 |
| 1.10 | 6.50 |
| 5.79 | 6.32 |
| 12.00 | 54.00 |
| 876.76 | 423.75 |
\----------------------------------------/
If you are sure that your table fields are capable of containing numeric values to more than zero decimal places (i.e. scale > 0), please post the CREATE TABLE script for the table (you can get this from SSMS) or a screenshot of the Column listing so we can see the true type of the underlying fields. It would also be useful to see values SELECTed from the fields without any CASTing so we can see how the data is presented without any conversion.

Why does this CQL return empty results with beginning and ending date range are the same value

I am writing a parameterized CQL statement that is not behaving as I would expect. I am trying to return a list of records for a range of dates that in this instance are using the same date for the beginning and end points.
The table is defined as follows:
CREATE TABLE sometable (
partition_key varchar,
cluster_start_date timestamp,
other_cluster_key varchar,
data varchar,
PRIMARY KEY((partition_key), cluster_start_date, other_cluster_key)
) WITH CLUSTERING ORDER BY(cluster_start_date ASC, other_cluster_key ASC);
The prepared statement is as follows:
SELECT * FROM sometable WHERE partition_key = 'xxx' AND cluster_start_date >= ? AND cluster_start_date <= ?;
When the parameterized dates are different, the statement returns the correct data. But when the parameterized dates are the same, then no values are returned.
Could someone tell me why this statement does not work with identical parameters, and if so, what can be done to get the expected behavior?
Thanks for all who can help....
You didn't show how you are specifying the timestamp value in your query, but I'm guessing you are specifying it in seconds. Internally the resoultion of timestamp is higher than seconds.
If you specify a timestamp in seconds, then your lower bound would be rounded down to 000 for the milliseconds, and your upper bound would also be rounded down to 000 milliseconds. It's likely the row you expect to see does not have 000 for the milliseconds, and so it is outside the tiny 1 millisecond wide range you specified. For example:
SELECT * from sometable ;
partition_key | cluster_start_date | other_cluster_key | data
---------------+--------------------------+-------------------+------
a | 2015-08-22 06:16:38-0400 | a | row1
a | 2015-08-22 06:17:09-0400 | b | row2
a | 2015-08-22 06:17:31-0400 | c | row3
SELECT * FROM sometable WHERE partition_key = 'a'
and cluster_start_date >= '2015-08-22 06:17:09-0400'
and cluster_start_date <= '2015-08-22 06:17:09-0400';
partition_key | cluster_start_date | other_cluster_key | data
---------------+--------------------+-------------------+------
(0 rows)
But if you specify the timestamp to millisecond precision, and use 999 for the milliseconds on the upper bound, it finds the row:
SELECT * FROM sometable WHERE partition_key = 'a'
and cluster_start_date >= '2015-08-22 06:17:09.000-0400'
and cluster_start_date <= '2015-08-22 06:17:09.999-0400';
partition_key | cluster_start_date | other_cluster_key | data
---------------+--------------------------+-------------------+------
a | 2015-08-22 06:17:09-0400 | b | row2
Or you could drop the equals on the upper bound and say less than the next second like this:
SELECT * FROM sometable WHERE partition_key = 'a'
and cluster_start_date >= '2015-08-22 06:17:09-0400'
and cluster_start_date < '2015-08-22 06:17:10-0400';
partition_key | cluster_start_date | other_cluster_key | data
---------------+--------------------------+-------------------+------
a | 2015-08-22 06:17:09-0400 | b | row2

Why is Latin1_General_CS_AS not case sensitive?

For LIKE queries, the Latin1_General_CS_AS collation is not case-sensitive. According to a bug report to Microsoft, this was listed as "By Design".
However, the Latin1_General_Bin collation is also case-sensitive and works exactly as expected for LIKE queries.
You can see the difference in this simple query:
SELECT
MyColumn AS Latin1_General_Bin
FROM MyTable
WHERE MyColumn LIKE '%[a-z]%' COLLATE Latin1_General_Bin;
SELECT
MyColumn AS Latin1_General_CS_AS
FROM MyTable
WHERE MyColumn LIKE '%[a-z]%' COLLATE Latin1_General_CS_AS;
SQL Fiddle Demo.
My questions are:
Why would this be considered "By Design" to be case-insensitive in LIKE?
If this really is better, why is it a different behavior between the two case sensitive collations _Bin and _CS_AS?
I was going to standardize on Latin1_General_CS_AS for any case-sensitive databases going forward, but this seems like a subtle query bug waiting to happen.
It is not a regular expression. The range [a-z] just means >='a' AND <='z'.
Under that collation that includes all letters except capital Z.
Under SQL_Latin1_General_CP1_CS_AS all except capital A fall within that sort order.
In case that is still not clear review the sort orders for the following; for the three different collations
SELECT *
FROM (VALUES ('A'),('B'),('Y'),('Z'), ('a'),('b'),('y'),('z')) V(C)
ORDER BY C COLLATE Latin1_General_Bin
You see that the binary collation has all the upper case letters together, the other two don't.
+--------------------+----------------------+-------------------------------+
| Latin1_General_Bin | Latin1_General_CS_AS | SQL_Latin1_General_CP1_CS_ASĀ  |
+--------------------+----------------------+-------------------------------+
| A | a | A |
| B | A | a |
| Y | b | B |
| Z | B | b |
| a | y | Y |
| b | Y | y |
| y | z | Z |
| z | Z | z |
+--------------------+----------------------+-------------------------------+
This is documented in BOL
In range searches, the characters included in the range may vary
depending on the sorting rules of the collation.

Resources