T-SQL :: how to remove CONVERT_IMPLICIT - sql-server

Pinal Dave posted a query that we can use to find the most polluting implicit conversion. The query works very well and in fact I found a very polluting one:
SUM(-[Quantité] * [Conditionnement]) AS QteVendueUQB,
This horror is:
Taking [Quantité] which has data type MONEY
Taking [Conditionnement] which has data type SMALLINT
Multiplying them together
The table looks like this:
Is there a better way to multiply these two values?

As #Larnu wrote in the comment:
If the data types are different, then one of them will be implicitly
converted using data type precedence. There is no way round that. It
is by design, and intended. If you don't want implicit conversion,
don't use different data types for data that will interact with each
other.

Related

Microsoft T-SQL: Can I CONVERT smallint to varchar

It may just be me, but... Despite the fact that most sql developers may consider cast & convert to be very basic stuff, and that may be true, I find Microsoft's documentation page on CAST & CONVERT to be one of the most hideous, not-intuitively-laid-out, hard to understand things I have ever seen. Much of their documentation is great. Like constantly trying to blend the entire page into a mix of both cast and convert, jumping back and forth in each sentence... rather than dealing with them separately. And who puts the target_type as the first argument? Putting the expression as the first argument would be more intuitive - and follow the other 99% of numerous programming languages' syntax. UH
MS says that I can only convert to 3 data types: (well actually I'm not really sure if this applies to both CAST and CONVERT, since they ARE, in fact, different... But according to the layout of that webpage, it apparently applies equally to both - even though I already know for a fact that it is not true for CAST, which I use much more frequently).
It says: "Is the target data type. This includes xml, bigint, and sql_variant"
Putting aside for the moment the fact that I CAST things as many other datatypes all the time (date, varchar),
My immediate question is: if I can only CONVERT to those data types, then why does this work?
select CONVERT(varchar(200), cast(50 as smallint))
And finally, I'd like to run an INSERT that will be getting a smallint and putting it into a varchar(200) column.
All I'm trying to do is avoid any failures, so maybe I don't really "need" to convert or cast over to varchar, but any commments on
answer on what is my apparent misunderstanding about the CONVERT documentation
or
how to safely convert it to insert to varchar
are welcome. As long as you're not just overly unpleasant, since there are always those MS fans who get hot under the collar at all critiques of MS .. :|
Yes, you can convert from smallint to varchar.
1) answer on what is my apparent misunderstanding about the CONVERT
documentation
This may be product of general lack of understanding on what data types are, how can they be converted from one type to another and equally important; what styles are when it comes to the aesthetic representation of a data type.
CAST is an explicit cast operation with no style options.
CONVERT is also an explicit cast that gives you the ability to specify a style for the output.
The documentation clearly states:
Implicit Conversions
Implicit conversions are those conversions that occur without
specifying either the CAST or CONVERT function. Explicit conversions
are those conversions that require the CAST or CONVERT function to be
specified. The following illustration shows all explicit and implicit
data type conversions that are allowed for SQL Server system-supplied
data types. These include xml, bigint, and sql_variant. There is no
implicit conversion on assignment from the sql_variant data type, but
there is implicit conversion to sql_variant.
For your second question
2) how to safely convert it to insert to varchar
Depending of what you mean by safe. Converting to varchar is the convertion that most likely succeed. But whenever to cast to any toher datatype you are intrinsically changing the very nature of the data and will lose precision when casting to smaller types (or applying styles).
The documentation clearly states:
Truncating and Rounding Results
When you convert character or binary expressions (char, nchar,
nvarchar, varchar, binary, or varbinary) to an expression of a
different data type, data can be truncated, only partially displayed,
or an error is returned because the result is too short to display.
Conversions to char, varchar, nchar, nvarchar, binary, and varbinary
are truncated, except for the conversions shown in the following
table.
in other words, casting is never safe.
Numbers always get silently truncated for me. I would propose:
Option 1
Compare the converted value with the original value.
DECLARE #ORIGINAL DECIMAL(13,2) = -99999999999.99 --
DECLARE #EXPECTED VARCHAR(15) = ''
SELECT #EXPECTED = CONVERT(VARCHAR(15),#ORIGINAL)
IF CONVERT(DECIMAL(13,2),#EXPECTED) != #ORIGINAL SELECT 'Ooops'
Option 2
Make sure that all possible values will fit in target varchar.
Decimal(13,2). Widest number possible will be "-99999999999.99" needs varchar(15):
13 chars for digits
1 char for decimal separator
1 char for minus sign
Smallint stores 2 bytes, from "-32768" to "32767", needs varchar(6):
- 5 chars for digits
- 1 char for minus sign
Not sure if you need chars for thousands separators, or if you can change it via settings.

Reading an XML file with an unkown format in C

How do I read from an XML file with an unkown format, for example a file can be of the format:
<data1>int1</data1>
<data2>int2</data2>
<data3>int3</data3>
<data4>int4</data4>
OR
<data1>int1</data1>
<data4>int4</data4>
OR
<data1>int1</data1>
<data2>int2</data2>
<data3>int3</data3>
<data4>int4</data4>
<data5>int5</data5>
In the second case I am to assume int2 and int3 are to be assigned default values. I thought of one way to solve this problem but it came out messy and spaghetti like.
Any help would be appreciated!
(1) None of those is a complete XML document. There needs to be a single root element.
(2) If you don't insist on restricting the number and order of their of instances, it's easy to declare in a DTD that element content can be a mix of other elements, using the '|' operator. (See http://www.w3.org/TR/xml11/#sec-element-content)
(3) If you want to constrain those more tightly, then yes, DTDs can require spelling out all the combinations. Switching to validating against XML Schemas is one obvious solution; DTDs are pretty much considered obsolete anyway since they aren't compatible with XML Namespaces (which have become a basic part of XML processing).
(4) If you insist on sticking with DTDs, and can't accept unconstrained order/count, and don't want to spell out all the possible sequences... consider doing some of that checking and/or applying of default values in your application code rather than in the DTD.

Totals in reports when some values are calculated? MS Access

I am using a report to show wastage calculations after converting our stock.
I am trying to get the totals to display at the bottom, however some of the columns are made up of fields that are calculated (such as sValue-Wastage)
For these fields I can't seem to use =Sum(rValue), it acts as though this is a variable that is to be decided by the user when I input this, it also adds square brackets around rValue (=Sum([rValue])).
Is there some different way of acheiving this that I need to know about? I am trying to get:
=Sum(Wastage)
Where Wastage is:
=[sValue]-[rValue]
Thanks,
Bob P
You must sum the two calculated fields.
=Sum([sValue]-[rValue])
From your comments, it appears that rValue holds a domain aggregate function. You can try:
=Sum([sValue]-DSum("sValue","[Stock Conversion Items]")
This will be okay if all SCIDs in [Stock Conversion Items] should be included, if not you may be able to add a WHERE statement. If the relationship is too complicated for a WHERE statement, consider basing your report on a query that includes [Stock Conversion Items].
It is an old story: http://support.microsoft.com/kb/208850

CAST as decimal, or *1.0?

Both of the following queries will give me the same result, and I've seen both techniques being used in the past to ensure a decimal data type is returned.
select CAST(5 as decimal(18,2))/2
select 5*1.0/2
Which method is best to use?
Why would people use *1.0 over casting? Is this just because its quicker to type?
If you want to control the precision or scale of the result, then you'll have to use CAST or CONVERT. But I believe that if you just want "a decimal", then as you suggested, it's just quicker to type * 1.0.

How can I find odd house numbers in MS SQL?

The problem is I need to ignore the stray Letters in the numbers: e.g. 19A or B417
Take a look here:
Extracting Numbers with SQL Server
There are several hidden "gotcha's" that are explained pretty well in the article.
It depends on how much data you're dealing with, but doing that in SQL is probably going to be slow. Not everyone will agree with me here, but I think all data processing should be done in application code.
I would just take the rows you want, and filter it in the application you're dealing with.
The easiest thing to do here would be to create a CLR function which takes the address. In the CLR function, you would take the first part of the address (assuming it is the house number), which should be delimited by whitespace.
Then, replace any non-numeric characters with an empty string.
You should have a string representing an integer at that point which you can pass to the Parse method on the Int32 class to produce an integer, which you can then check to see if it is odd.
I recommend a CLR function (assuming you are using SQL Server 2005 and above, and can set the compatibility level of the database) here because it's easier to perform string manipulations in .NET than it is in T-SQL.
Assuming [Address] is the column with the address in it...
Select Case Cast(Substring(Reverse(Address), PatIndex('%[0-9]%',
Reverse(Address)), 1) as Integer) % 2
When 0 Then 'Even'
When 1 Then 'Odd' End
From Table
I've been through this drill before. The best alternative is to add a column to the table or to a subsidiary joinable table that stores the inferred numerical value for the purpose. Then use iterative queries to set the column repeatedly until you get sufficient accuracy and coverage. You'll end up encountering stuff like "First, Third," "451a", "1200 South 19th Blvd East", and worse.
Then filter new and edited records as they occur.
As usual, UDF's should be avoided as being slow and (comparatively) less debuggable.

Resources