SQL Server Inner Query Problem - sql

I have a very simple query in SQL Server. But it is giving errors.
select * from(
select emp_name + ' ' + emp.surname as employee from ca_contact
)
This query is not working.
But when I write like the below, it is working:
select emp_name + ' ' + emp.surname as employee from ca_contact

You'd need an alias. In this case foobar
select * from
(select emp_name + ' ' + emp.surname as employee from ca_contact) foobar

I think you need to specify table alias --
select * from(
select emp_name + ' ' + emp.surname as employee from ca_contact
) t1

In SQL Server, all derived tables must be given an alias [exception is if do not select anything from them, e.g. in an IN/EXISTS clause]. An alternative for what you are doing from SQL Server 2005 onwards is to use a Common Table Expression, which incidentally is also now available in recent versions of Oracle.
A simple meaningless alias
select * from
(select emp_name + ' ' + surname as employee from ca_contact) [ ]
A Common Table Expression, also naming the column at the sametime
;WITH [ ](employee) AS (
select emp_name + ' ' + surname
from ca_contact)
select * from [ ]
FWIW, you can omit the CTE column names and derive them from the query
WITH [ ] AS (
select emp_name + ' ' + surname as employee
from ca_contact)
select * from [ ]
Note: Not sure how you can have emp.surname since there is no table/alias emp defined in your query

Please try the below query
select employee from (select emp_name + ' ' + emp.surname as employee from ca_contact) as test

Related

SQL Group by with string concat and CASE statements

I am trying to understand SQL Group By concept along with the Select statement having:
String concat operation
CASE statement
I understand the Select clause is evaluated after the Group by clause and that any non aggregated columns in the Select list also need to be present in the Group By list. I will try to explain my questsion with an example ORDERS table that has one record for each order:
SELECT FIRSTNAME +' '+LASTNAME
,EMP_TITLE
,EMAILADDR
,INACTIVE
,CASE WHEN ISNULL(EMAILADDR,'')='' THEN 'ADMIN#MYCOMPANY.COM' ELSE EMAILADDR END AS ALTEMAIL
,CASE WHEN AGE>60 THEN 'SENIOR' ELSE 'EMP' END AS EMPTYPE
,COUNT(*) AS TOTAL_ORDERS
FROM ORDERS
GROUP BY FIRSTNAME + ' '+ LASTNAME
,EMP_TITLE
,EMAILADDR
,INACTIVE
,CASE WHEN ISNULL(EMAILADDR,'')='' THEN 'ADMIN#MYCOMPANY.COM' ELSE EMAILADDREND
,CASE WHEN AGE>60 THEN 'SENIOR' ELSE 'EMP' END
I understand the Select clause is evaluated after the Group by clause. What I am confused/trying to get my head around is - in what situations do we use the column name vs the contents from the select statement (concat/case in this example) in the group by clause?
Example: In group by above, we could get rid of FIRSTNAME + ' '+ LASTNAME and replace it with FIRSTNAME,LASTNAME. Also we could replace the CASE statements with the EMAILADDR, AGE.
Question:
I am trying to understand in which situations this is OK to do and in which situations is it absolutely necessary that we must place the CASE statement (or the concat string operation) from select clause into the Group By clause?
In situations where it is OK to use either, what is the best practice to follow? Use the case statement as is from select in the group by? Or only use column names involved in the case statement?
The GROUP BY clause defines expressions that can be referenced without aggregation in the SELECT. So, this is allowed:
SELECT FIRSTNAME + ' ' + LASTNAME
. . .
GROUP BY FIRSTNAME, LASTNAME
because FIRSTNAME and LASTNAME can be referenced without aggregation functions. So, any expressions are allowed as well.
On the other hand, this is not allowed:
SELECT FIRSTNAME, LASTNAME
. . .
GROUP BY
In this case, FIRSTNAME and LASTNAME are not in the GROUP BY, so they are not allowed. This is also allowed:
SELECT HONORIFIC + ' ' + FIRSTNAME + ' ' + LASTNAME
. . .
GROUP BY FIRSTNAME + ' ' + LASTNAME, HONORIFIC
However, this is not allowed:
SELECT HONORIFIC + ' ' + FIRSTNAME + ' ' + LASTNAME
. . .
GROUP BY FIRSTNAME + ' ' + LASTNAME + HONORIFIC

Manipulating duplicate values?

I have a table, with an ID, FirstName & Lastname.
I'm selecting that using the following query:
SELECT USER_ID as [ID], First_P + ' ' + Last_P as FullName FROM Persons
It's working fine. I'm basically having a list of ID's and full names.
Full names could be the same. How is it possible for me to find them and add the ID on the Full name cell as well? only when the names are the same.
Example:
1 John Wick (1)
50 John Wick (50)
I haven't found any similar questions to be honest, at least not for MSSQL. So If there are any, feel free to link me.
please take a look my answer. I used nested query to identify number of duplicated names
SELECT
ID,
IIF(NUMBEROFDUPS =1, NAME, CONCAT(NAME, ' (', ID, ')')) AS NAME
FROM
(
SELECT
ID,
CONCAT(First_P, ' ', Last_P) AS NAME,
COUNT(*) OVER (PARTITION BY First_P,Last_P) AS NUMBEROFDUPS
FROM
Table1
) tmp;
You can use outer apply to group the items via First_P + ' ' + Last_P
and then add case for multiple items.
The select stuff should look like:
SELECT USER_ID as [ID], p1.First_P + ' ' + p1.Last_P + case when cnt.FullName is not null
then '(' + p2.[sum] + ')' else '' end as FullName FROM Persons p1
outer apply (select First_P + ' ' + Last_P as FullName,count(1) as [sum]
from Persons p2
where p2.First_P + ' ' + p2.Last_P = p1.First_P + ' ' + p1.Last_P
group by First_P + ' ' + Last_P
having count(1) > 1) cnt

Add Int Column Values Similar To Count

I have a data structure similiar to:
Person | Counter
Mary 1
Mary 2
John 5
John 6
I am trying to Group By on the Person column, and add up the values for the counter, for each person. Meaning, I am trying to return:
Person - Mary - Has 3 Counters
Person - John - Has 11 Counters
This is my current effort, however it is not working:
SELECT ('Person - '+ ISNULL(NULLIF([Person],''),'NOT ASSIGNED') + 'Has') as Name,
COUNT(*) Match, (SELECT COUNT(*) + 'Counters' FROM [MyTable] ) Total
FROM [MyTable]
Here you go:
DECLARE #DATA TABLE (Person VARCHAR(25), [Counter] INT)
INSERT INTO #DATA
SELECT 'Mary',1 UNION
SELECT 'Mary',2 UNION
SELECT 'John',5 UNION
SELECT 'John',6
SELECT 'Person - ' + ISNULL(Person,'Not Assigned') + ' - has ' + CAST(SUM([Counter]) AS VARCHAR) + ' counters'
from #DATA
GROUP BY Person
What you want is a group by clause just as you stated, so something based on this:
SELECT Person, SUM(Counter)
FROM MyTable
GROUP BY Person
I didn't include the extra text from your original query to make it a bit more compact.
Cheruvian's answer might be what you want, I can't tell exactly by your example, here is what I came up with just in case.
SELECT 'Person - ' + ISNULL(NULLIF(Person, ''), 'NOT ASSIGNED') + ' - Has ' + SUM(Counter) + ' Counters' As ReportOutput
FROM MyTable
GROUP BY Person
ORDER BY Person;
You should be using an aggregate function for this.
SELECT ('Person - '+ ISNULL(NULLIF([Person],''),'NOT ASSIGNED') + 'Has') as Name,
COUNT(*) As Match,
Sum(Counter) As Count
FROM [MyTable]
Group By Person
You were also missing an As in the second line. The Sum Aggregate function will Sum all of the values for each group, in this case your counter.
I would do this using a GROUP BY in a subselect, and then build the string using the results. Something like this:
SELECT ('Person - '+ myperson + ' Has ' + CAST(mycount AS VARCHAR) + ' Counters')
FROM (SELECT ISNULL(NULLIF([Person],''),'NOT ASSIGNED') myperson, sum(counter) mycount
FROM [MyTable]
GROUP BY ISNULL(NULLIF([Person],''),'NOT ASSIGNED')) a

How to combined first_name and last_name using sql query?

There are two columns in database as name FIRST_NAME and LAST_NAME.i want to join both the name and display in single column. The query which is used by me is given below but it gives error as Incorrect syntax near 'NAME'.
Modify the query:
SELECT [CREATED_ON], MUD.PK_ID AS USER_ID,
(MUD.FIRST_NAME + ' ' + MUD.LAST_NAME NAME)
AS NAME FROM USER_TABLE
Remove the erroneous 'NAME'. e.g.
SELECT [CREATED_ON], MUD.PK_ID AS USER_ID, (MUD.FIRST_NAME + ' ' + MUD.LAST_NAME)
AS NAME FROM USER_TABLE
SELECT
[CREATED_ON],
MUD.PK_ID AS USER_ID,
(MUD.FIRST_NAME + ' ' + MUD.LAST_NAME) AS [NAME]
FROM
USER_TABLE
The parentheses around the expression MUD.FIRST_NAME + ' ' + MUD.LAST_NAME are optional.

How do I perform a GROUP BY on an aliased column in SQL Server?

I'm trying to perform a group by action on an aliased column (example below) but can't determine the proper syntax.
SELECT LastName + ', ' + FirstName AS 'FullName'
FROM customers
GROUP BY 'FullName'
What is the correct syntax?
Extending the question further (I had not expected the answers I had received) would the solution still apply for a CASEed aliased column?
SELECT
CASE
WHEN LastName IS NULL THEN FirstName
WHEN LastName IS NOT NULL THEN LastName + ', ' + FirstName
END AS 'FullName'
FROM customers
GROUP BY
LastName, FirstName
And the answer is yes it does still apply.
You pass the expression you want to group by rather than the alias
SELECT LastName + ', ' + FirstName AS 'FullName'
FROM customers
GROUP BY LastName + ', ' + FirstName
This is what I do.
SELECT FullName
FROM
(
SELECT LastName + ', ' + FirstName AS FullName
FROM customers
) as sub
GROUP BY FullName
This technique applies in a straightforward way to your "edit" scenario:
SELECT FullName
FROM
(
SELECT
CASE
WHEN LastName IS NULL THEN FirstName
WHEN LastName IS NOT NULL THEN LastName + ', ' + FirstName
END AS FullName
FROM customers
) as sub
GROUP BY FullName
Unfortunately you can't reference your alias in the GROUP BY statement, you'll have to write the logic again, amazing as that seems.
SELECT LastName + ', ' + FirstName AS 'FullName'
FROM customers
GROUP BY LastName + ', ' + FirstName
Alternately you could put the select into a subselect or common table expression, after which you could group on the column name (no longer an alias.)
Sorry, this is not possible with MS SQL Server (possible though with PostgreSQL):
select lastname + ', ' + firstname as fullname
from person
group by fullname
Otherwise just use this:
select x.fullname
from
(
select lastname + ', ' + firstname as fullname
from person
) as x
group by x.fullname
Or this:
select lastname + ', ' + firstname as fullname
from person
group by lastname, firstname -- no need to put the ', '
The above query is faster, groups the fields first, then compute those fields.
The following query is slower (it tries to compute first the select expression, then it groups the records based on that computation).
select lastname + ', ' + firstname as fullname
from person
group by lastname + ', ' + firstname
Given your edited problem description, I'd suggest using COALESCE() instead of that unwieldy CASE expression:
SELECT FullName
FROM (
SELECT COALESCE(LastName+', '+FirstName, FirstName) AS FullName
FROM customers
) c
GROUP BY FullName;
My guess is:
SELECT LastName + ', ' + FirstName AS 'FullName'
FROM customers
GROUP BY LastName + ', ' + FirstName
Oracle has a similar limitation, which is annoying. I'm curious if there exists a better solution.
To answer the second half of the question, this limitation applies to more complex expressions such as your case statement as well. The best suggestion I've seen it to use a sub-select to name the complex expression.
You can use CROSS APPLY to create an alias and use it in the GROUP BY clause, like so:
SELECT FullName
FROM Customers
CROSS APPLY (SELECT LastName + ', ' + FirstName AS FullName) Alias
GROUP BY FullName
SELECT
CASE
WHEN LastName IS NULL THEN FirstName
WHEN LastName IS NOT NULL THEN LastName + ', ' + FirstName
END AS 'FullName'
FROM
customers
GROUP BY
LastName,
FirstName
This works because the formula you use (the CASE statement) can never give the same answer for two different inputs.
This is not the case if you used something like:
LEFT(FirstName, 1) + ' ' + LastName
In such a case "James Taylor" and "John Taylor" would both result in "J Taylor".
If you wanted your output to have "J Taylor" twice (one for each person):
GROUP BY LastName, FirstName
If, however, you wanted just one row of "J Taylor" you'd want:
GROUP BY LastName, LEFT(FirstName, 1)
If you want to avoid the mess of the case statement being in your query twice, you may want to place it in a User-Defined-Function.
Sorry, but SQL Server would not render the dataset before the Group By clause so the column alias is not available. You could use it in the Order By.
In the old FoxPro (I haven't used it since version 2.5), you could write something like this:
SELECT LastName + ', ' + FirstName AS 'FullName', Birthday, Title
FROM customers
GROUP BY 1,3,2
I really liked that syntax. Why isn't it implemented anywhere else? It's a nice shortcut, but I assume it causes other problems?
SELECT
CASE WHEN LastName IS NULL THEN FirstName
WHEN LastName IS NOT NULL THEN LastName + ', ' + FirstName
END AS 'FullName'
FROM customers GROUP BY 1`
For anyone who finds themselves with the following problem (grouping by ensuring zero and null values are treated as equals)...
SELECT AccountNumber, Amount AS MyAlias
FROM Transactions
GROUP BY AccountNumber, ISNULL(Amount, 0)
(I.e. SQL Server complains that you haven't included the field Amount in your Group By or aggregate function)
...remember to place the exact same function in your SELECT...
SELECT AccountNumber, ISNULL(Amount, 0) AS MyAlias
FROM Transactions
GROUP BY AccountNumber, ISNULL(Amount, 0)