TSQL syntax - Is aliasing with a quoted prefix depricated? - tsql

Can anyone tell me if writing a query in the following tsql syntax is either (1) currently -- or going to be soon -- deprecated by MSFT, or (2) in opposition to some best practice of which I'm not aware?
SELECT
'CustName' = (SELECT Lastname + ', ' + Firstname FROM Cust WHERE CustID = O.CustID),
'ProdName' = (SELECT ProductName FROM Product WHERE ProductID = O.ProductID)
FROM Orders O
The specific question is putting the new column name all the way to the "front" or left of the line as opposed to writing the subquery and putting the new column name in square brackets after the subquery. Obviously both will work, but the DBAs reviewing my database code typically give me a WTF look when they see this, even though I tell them it's far more readable because all of your column names are on the left...
Is there something wrong with writing queries in this manner?
CLARIFICATION: The point isn't the subqueries in the SELECT statement, it's whether the syntax:
'NewColumnName' = OldColumnName
is going away anytime soon. I chose to demonstrate the question with a pair of subqueries rather than using the giant and esoteric custom function calls and case statements that are actually in the production code I'm using.

According to SQL 2005 BOL here (ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.en/instsql9/html/c10eeaa5-3d3c-49b4-a4bd-5dc4fb190142.htm) and here: http://msdn.microsoft.com/en-us/library/ms143729(SQL.90).aspx and in the 2008 doc here: http://msdn.microsoft.com/en-us/library/ms143729.aspx (look under "Transact-SQL" features) this will be deprecated in a future release (unspecified).
However, it's a bit subtle. This deprecation warning actually only applies to the use of the quotation marks in this context, not the column-alias-first format. I.E., this will be deprecated:
'AliasName' = NewValue
However, this is still valid and even listed as a replacement for it:
AliasName = NewValue
So just take the apostrophes out and you're good.

There's absolutely nothing wrong with the
column alias = expression
syntax and it isn't deemed to be either deprecated soon or bad practice!
The main problem with the query, is that you should be using joins to connect the Orders table to the Cust and Product tables. The query looks like you are assuming there is only one Customer and one Product per order - think about what would happen if that wasn't true....
SELECT
'CustName' = C.Lastname + ', ' + C.Firstname,
'ProdName' = P.ProductName
FROM Orders O
JOIN Cust C on C.CustID = O.CustID
JOIN Product P on P.ProductID= O.ProductID

I typically see this syntax when using a subquery to set a variable, #MyValue = (subquery) for example. So there was a bit of a WTF from me for a minute as well. However, I see your point, and overall I can't imagine that it is something that would be not supported in the future.
Personally though I prefer a more formatted manner, and a distinct "AS" definition. I would write it something like this.
SELECT
(SELECT Lastname + ', ' + Firstname
FROM Cust
WHERE CustID = O.CustID
) AS CustName,
(SELECT ProductName
FROM Product
WHERE ProductID = O.ProductID
) AS ProdName
FROM Orders O
I personally find this easier to read....but more than likely that is just me...

My guess is people balk at it because it's specific to MSSQL, not supported by other systems (at least, not the ones I have available to me right now). It's like listening to an unfamiliar dialect - you can see what the person is getting at, but you can't see why on Earth they would choose to speak that way.

You are aliasing a column name in a strange, unfamiliar way. i had to run it to see if it was even valid.
Rather than doing what's strange and uncommon, do what people expect:
SELECT
(SELECT Lastname + ', ' + Firstname FROM Cust WHERE CustID = O.CustID) AS CustName,
(SELECT ProductName FROM Product WHERE ProductID = O.ProductID) AS ProdName
FROM Orders O

Related

T-SQL: Find column match within a string (LIKE but different)

Server: SQL Server 2008 R2
I apologize in advance, as I'm not sure of the best way to verbalize the question. I'm receiving a string of email addresses and I need to see if, within that string, any of the addresses exist as a user already. The query that obviously doesn't work is shown below, but hopefully it helps to clarify what I'm looking for:
SELECT f_emailaddress
FROM tb_users
WHERE f_emailaddress LIKE '%user1#domain.com,user2#domain.com%'
I was hoping SQL had an "InString" operator, that would check for matches "within the string", but I my Google abilities must be weak today.
Any assistance is greatly appreciated. If there simply isn't a way, I'll have to dig in and do some work in the codebehind to split each item in the string and search on each one.
Thanks in advance,
Beems
Split the input string and use IN clause
to split the CSV to rows use this.
SELECT Ltrim(Rtrim(( Split.a.value('.', 'VARCHAR(100)') )))
FROM (SELECT Cast ('<M>'
+ Replace('user1#domain.com,user2#domain.com', ',', '</M><M>')
+ '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
Now use the above query in where clause.
SELECT f_emailaddress
FROM tb_users
WHERE f_emailaddress IN(SELECT Ltrim(Rtrim(( Split.a.value('.', 'VARCHAR(100)') )))
FROM (SELECT Cast ('<M>'
+ Replace('user1#domain.com,user2#domain.com', ',', '</M><M>')
+ '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a))
Or use can use Inner Join
SELECT f_emailaddress
FROM tb_users A
JOIN (SELECT Ltrim(Rtrim(( Split.a.value('.', 'VARCHAR(100)') )))
FROM (SELECT Cast ('<M>'
+ Replace('user1#domain.com,user2#domain.com', ',', '</M><M>')
+ '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)) B
ON a.f_emailaddress = b.f_emailaddress
You first need to split the CSV list into a temp table and then use that to INNER JOIN with your existing table, as that will act as a filter.
You cannot use CONTAINS unless you have created a Full Text index on that table and column, which I doubt is the case here.
For example:
CREATE TABLE #EmailAddresses (Email NVARCHAR(500) NOT NULL);
INSERT INTO #EmailAddress (Email)
SELECT split.Val
FROM dbo.Splitter(#IncomingListOfEmailAddresses);
SELECT usr.f_emailaddress
FROM tb_users usr
INNER JOIN #EmailAddresses tmp
ON tmp.Email = usr.f_emailaddress;
Please note that the reference to "dbo.Splitter" is a placeholder for whatever string splitter you already have or might get. Please do not use any splitter that makes use of a WHILE loop. The best options are either the SQLCLR- or XML- based ones. The XML-based ones are generally fast but do have some issues with encoding if the string to be split has special XML characters such as &, <, or ". If you want a quick and easy SQLCLR-based splitter, you can download the Free version of the SQL# library (which I am the creator of, but this feature is in the free version) which contains String_Split and String_Split4k (for when the input is always <= 4000 characters).
SQL has a CONTAINS and an IN function. You can use either of those to accomplish your task. Click on either for more information via MSDNs website! Hope this helps.
CONTAINS
CONTAINS will look to see if any values in your data contain the entire string you provided. Kind of similar in presentations to LIKE '%myValue%';
SELECT f_emailaddress
FROM tb_users
WHERE CONTAINS (f_emailaddress, 'user1#domain.com');
IN
IN will return matches for any values in the provided comma delimited list. They need to be exact matches however. You can't provide partial terms.
SELECT f_emailaddress
FROM tb_users
WHERE f_emailaddress IN ('user1#domain.com','user2#domain.com')
As far as splitting each of the values out into separate strings, have a look at the StackOverflow question found HERE. This might point you in the proper direction.
You can try like this(not tested).
Before using this, make sure that you have created a Full Text index on that table and column.
Replace your comma with AND then
SELECT id,email
FROM t
where CONTAINS(email, 'user1#domain.com and user2#domain.com');
--prepare temp table for testing
DECLARE #tb_users AS TABLE
(f_emailaddress VARCHAR(100))
INSERT INTO #tb_users
( f_emailaddress)
VALUES ( 'user1#domain.com' ),
( 'user2#domain.com' ),
( 'user3#domain.com' ),
( 'user4#domain.com' )
--Your query
SELECT f_emailaddress
FROM #tb_users
WHERE 'user1#domain.com,user2#domain.com' LIKE '%' + f_emailaddress + '%'

TSQL Keyword Previous or Last or something similar

This question is geared for those who have more SQL experience than me.
I am writing a query(that will eventually be a Stored Procedure but this should be irrelevant) where I want to select the count of rows if the most recent entry's is equivalent to the one that was just entered before. And i want to continue to do this until it hits an entry that has a different value. (Poorly explained so I will show the example)
In my table I have a column 'Product_Id' and when this query is run i want it take the product_id and compare it to the previously entered product Id, if its the same I want to add one, and I want it to keep checking the previously entered product_id until it runs into a different product_id
I'm hoping it sounds more complicated than it is, and the query would look something like
Select count(Product_ID)
FROM dbo.myTable
Where Product_Id = previous(Product_Id)
Now, i know that previous isn't a keyword in TSQL, and neither was Last, but I'm hoping of someone who knows a keyword that does what I am asking.
Edit for Sam
USE DbName;
GO
WITH OrderedCount as
(
select ROW_NUMBER() OVER (Order by dbo.Line_Production.Run_Date DESC) as RowNumber,
Line_Production.Product_ID
From dbo.Line_Production
)
Select RowNumber, COUNT(OrderedCount.Product_ID) as PalletCount
From OrderedCount
WHERE OrderedCount.RowNumber + 1 = RowNumber
and Product_ID = Product_ID
Group by RowNumber
The OrderedCount portion works, and it returns the data back how I want it, I'm now having trouble comparing the Product_ID's for different RowNumbers
my Where Clause is wrong
There's no keyword. That would be a nice magic solution, but it doesn't exist, at least in part because there is no guaranteed ordering (okay, you could have the keyword only if there is an ORDER BY...). I can write you a query, but that'll take time, so for now I'll give you a few steps and I'll come back and see if you still need help in a bit.
Figure out an ORDER BY, otherwise no order is guaranteed. If there is a time entered field, that's a good choice, or an index, that works too.
Learn to use Row_Number.
Compare the table (with Row_Number) to itself where instance1.row - 1 = instance2.row.
If product_id is an identity column, couldn't you just do product_id - 1? In other words, if it's sequential, it's the same as using ROW_NUMBER mentioned in the previous comment.

Postgresql Faulty Syntax on select/join/group

What about the following is not proper syntax for Postgresql?
select p.*, SUM(vote) as votes_count
FROM votes v, posts p
where p.id = v.`voteable_id`
AND v.`voteable_type` = 'Post'
group by v.voteable_id
order by votes_count DESC limit 20
I am in the process of installing postgresql locally but wanted to get this out sooner :)
Thank you
MySQL is a lot looser in its interpretation of standard SQL than PostgreSQL is. There are two issues with your query:
Backtick quoting is a MySQL thing.
Your GROUP BY is invalid.
The first one can be fixed by simply removing the offending quotes. The second one requires more work; from the fine manual:
When GROUP BY is present, it is not valid for the SELECT list expressions to refer to ungrouped columns except within aggregate functions, since there would be more than one possible value to return for an ungrouped column.
This means that every column mentioned in your SELECT either has to appear in an aggregate function or in the GROUP BY clause. So, you have to expand your p.* and make sure that all those columns are in the GROUP BY, you should end up with something like this but with real columns in place of p.column...:
select p.id, p.column..., sum(v.vote) as votes_count
from votes v, posts p
where p.id = v.voteable_id
and v.voteable_type = 'Post'
group by p.id, p.column...
order by votes_count desc
limit 20
This is a pretty common problem when moving from MySQL to anything else.

join multiple fields to one table

I have 2 tables (there are more but un related to question) optionValue and productStock
I want to get the option names from the optionValue table for each option1, option2, option3 (the query below will should help to make more sense)
below is my attempt, the current query it only works if all options are set but is returns null if any option is not set:
SELECT s.option1, n1.name s.optionName1,
s.option2, n2.name s.optionName2,
s.option3, n3.name s.optionName3
FROM productStock as s
INNER JOIN optionValue n1 on s.option1 = v1.optionValueID
INNER JOIN optionValue n2 on s.option2 = v2.optionValueID
INNER JOIN optionValue n3 on s.option3 = v3.optionValueID
WHERE s.productStockID = 1
I understand why it doesn't work because when the option is null ther is no matches to the optionValue table but im not sure how to fix it (if it is fixable)
I read in a couple of places about using IN or COALESCE but I don't understand how to use them.
It seems like some of your syntax is a little incorrect.
Apart from that you want LEFT OUTER JOIN instead of INNER JOIN.
Visual explanation of SQL joins.
What you really need first it to correct your database design. Anytime you have fields like this:
s.option1,s.option2, s.option3
Then what you really need is a child table to store the information. What happens when you need 6 options or 25? This is a very bad database design and will cause no end of problems incuding the inefficent query you now have to write. This is a cancer at the heart of your system and needs to be fixed before anything else is done.

Can UNION ALL be faster than JOINs or do my JOINs just suck?

I have a Notes table with a uniqueidentifier column that I use as a FK for a variety of other tables in the database (don't worry, the uniqueidentifier columns on the other tables aren't clustered PKs). These other tables represent something of a hierarchy of business objects. As a simple representation, let's say I have two other tables:
Leads (PK LeadID)
Quotes (PK QuoteID, FK LeadID)
In the display of a Lead in the application, I need to show all notes related to the lead, including those tagged to any Quote that belongs to that lead. I have two options as far as I can see — either a UNION ALL or several LEFT JOIN statements. Here's how they'd look:
SELECT N.*
FROM Notes N
JOIN Leads L ON N.TargetUniqueID = L.UniqueID
WHERE L.LeadID = #LeadID
UNION ALL
SELECT N.*
FROM Notes N
JOIN Quotes Q ON N.TargetUniqueID = Q.UniqueID
WHERE Q.LeadID = #LeadID
Or...
SELECT N.*
FROM Notes N
LEFT JOIN Leads L ON N.TargetUniqueID = L.UniqueID
LEFT JOIN Quotes Q ON N.TargetUniqueID = Q.UniqueID
WHERE L.LeadID = #LeadID OR Q.LeadID = #LeadID
In real life I have a total of five tables that the notes could be attached to, and that number could grow as the application grows. I already have non-clustered indexes set up on the uniqueidentifier columns I'm using, and SQL Profiler says I can't make any more improvements, but when I do a performance test on a realistically-sized test data set, I get the following numbers:
UNION ALL — 0.010 sec
LEFT JOIN — 0.744 sec
I had always heard that using UNION was bad, and that UNION ALL was only marginally better, but the performance numbers don't seem to bear that out. Granted, the UNION ALL SQL code might be more of a pain to maintain, but at that kind of performance difference it's probably worth it.
So is UNION ALL really better here or am I missing something on the LEFT JOIN code that is slowing things down?
The UNION ALL version would probably be satisfied quite easily by 2 index seeks. OR can lead to scans. What do the execution plans look like?
Also have you tried this to avoid accessing Notes twice?
;WITH J AS
(
SELECT UniqueID FROM Leads WHERE LeadID = #LeadID
UNION ALL
SELECT UniqueID FROM Quotes WHERE LeadID = #LeadID
)
SELECT N.* /*Don't use * though!*/
FROM Notes N
JOIN J ON N.TargetUniqueID = J.UniqueID
I may be wrong, but I think that you will get a better performance if you rewrite you JOIN version to
SELECT N.*
FROM Notes N
LEFT JOIN Leads L ON N.TargetUniqueID = L.UniqueID AND L.LeadID = #LeadID
LEFT JOIN Quotes Q ON N.TargetUniqueID = Q.UniqueID AND Q.LeadID = #LeadID
WHERE Q.LeadID IS NOT NULL OR L.LeadID IS NOT NULL
In my experience, SQL Server is really bad with join conditions containing OR. I also use UNIONs in that case, and I got similar results like you (maybe half a second instead of 20).
Who said UNIONS are bad? Especially if you use UNION ALL, there should not be a performance hit, as UNION would have to go through the result to only keep unique records (actually doing something like distinct or group by).
You second query wouldn' even give correct results as it would covert the left joins to inner joins, see here for explantion as to why your syntax is bad:
http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN
UNION is slower, but UNION ALL should be pretty quick, right?