Dynamic Aliases in the SQL statement - sql

I want to display Alias name based on the value of some other column name in the query in SQL Server. For e.g.
SELECT P.Amount AS (CASE P.Type WHEN 'Individual' THEN 'Salary' ELSE 'Profit' END)
FROM Person P
I know the above is not right, but something like this will help.

I'm not sure if you can add dynamic aliases, but you should be able to do something like this (if you have only a few possible aliases):
SELECT
CASE P.Type WHEN 'Individual' THEN P.Amount ELSE NULL END AS Salary,
CASE P.Type WHEN 'Individual' THEN NULL ELSE P.Amount END AS Profit
FROM
Person p

The "Alias" name is the name of the entire column of the data you are returning. It is not possible for this to change on a "by row" basis.
The only way you can change a column name (alias) dynamically is using dynamic SQL to build up your query. However, this does not appear to be what you are wanting to do.

You'd need to return Amount as "Amount" and then return an extra column containing the "type" for that amount.
i.e.g
SELECT P.Amount, CASE P.Type WHEN 'Individual' THEN 'Salary' ELSE 'Profit' END AS AmountType
FROM Person P

No can do...
SQL returns a recorset which can only have one name / alias per each column.
Which name would it choose it example given if some records returned by the query were 'Individual' and some were some other type?
Of course, as suggested in several responses, you can modify the number of columns returned by the query and name each column as desired but dealing with such a results set that may then require additional logic, which would defeat the purpose since if one wanted extra logic, he/she may just select both the Amount and the Type and work off these values for attribute naming and such at the level of the application...

A column can have one and only one name. If your rowset contained only one row, then you could look at the row's Type column first and then change the column name appropriately for the select. If it contains multiple rows, it's simply not possible.
IF 1 = (SELECT COUNT(*) FROM Person P WHERE <where-criteria>) THEN
IF 'Individual' = (SELECT P.Type FROM Person P WHERE <where-criteria>) THEN
SELECT P.Amount AS Salary
FROM Person P
WHERE <where-criteria>
ELSE
SELECT P.Amount AS Profit
FROM Person P
WHERE <where-criteria>
END IF
ELSE
SELECT P.Amount AS SalaryOrProfit
FROM Person P
WHERE <where-criteria>
END IF
I think you might need to re-examine your design.

Related

What's wrong with the following SQL query?

I have the following SQL query which finds the name and age of the oldest snowboarders.
SELECT c.cname, MAX(c.age)
FROM Customers c
WHERE c.type = 'snowboard';
But the guide I am reading says that query is wrong because the non-aggregate columns in the SELECT clause must come from the attributes in the GROUP BY clause.
I think this query serves its purpose because the aggregate MAX(c.age) corresponds to a single value. It does find the age of the oldest snowboarder.
You need to group by c.cname column. Whenever you do any aggregation on some column like SUM, COUNT, etc. you need to provide another column(s) by which you want to aggregate. You generally provide these columns in your SELECT clause(here c.cname). These same columns should be mentioned in the GROUP BY clause else you will get a syntax error.
The form should be
SELECT A, B, C, SUM(D)
FROM TABLE_NAME
GROUP BY A, B, C;
Your query should be like below
SELECT c.cname, MAX(c.age)
FROM Customers c
WHERE c.type=‘snowboard’
GROUP BY c.cname;
If you want to display the name and age of the oldest snowboarder(s), you have to do this:
SELECT c.cname, c.age
FROM Customers c
WHERE c.type = 'snowboard'
AND c.age = (SELECT MAX(age)
FROM Customers
WHERE type = 'snowboard')
Whenever some other table attributes are selected along with the aggregate function then it has to be accompanied by group by clause, otherwise a situation may occur where database may not know which row has to be selected. Let us say in your example there are two customers who have same maximum age, now database will try to pull out the age from both the rows but it will get confused, which name to pick. Here your group by clause comes into picture, which will instruct to display two different rows with different customer names but same maximum age.
Thus, your query should look like this:
SELECT c.cname, MAX(c.age)
FROM Customers c
WHERE c.type=‘snowboard’
GROUP BY c.cname;

SQLite referring to a named column from within another column

I've got a situation where I'm using a named column as such:
SELECT title, SUBSTR(title, 1, 2) as nameprefix FROM employees
Now the issue is that I'd like to add another named column and somehow refer to nameprefix in it. Is this possible? Essentially I would like to do this:
SELECT title, SUBSTR(title, 1, 2) as nameprefix,
(CASE WHEN nameprefix = 'Dr' THEN 'FOUND' ELSE 'NOPE' END) as flag
FROM employees
It complains with an error: Error: no such column: nameprefix. The actual use-case involves complex sub-queries and I'd like to be able to re-use their results rather than duplicating these sub-queries in each column that requires the result.
this is possible:
select title, nameprefix from
(
select title, SUBSTR(title, 1, 2) as nameprefix FROM employees
)
where nameprefix = 'Dr'
Sounds like you wish to view the table as if the first two characters of the title field are considered a separate, named column. So, I'd use CREATE VIEW to build exactly that view and then query against it, rather than against the underlying table.
So, in your case, issue something like
CREATE VIEW EmployeesWithCols (Name, Title, NamePrefix) AS
SELECT Name, Title, SUBSTR(title, 1, 2) FROM Employee
Now, simply replace the table employee in your original query with employeeswithcols and you will magically have a new NamePrefix column available. Of course, you'll probably need some additional columns in the view as well.
As a side note, I generally name views that are used only to add virtual computed columns to a single table with the table name and prefix "Ex" (as per many internal Windows functions) but in this case and many others that naming convention is ripe for embarrassment if you are not rigorous about the letter case in which you write the view name.
You can use alias in select you must repeat the code
SELECT title, SUBSTR(title, 1, 2) as nameprefix,
CASE WHEN SUBSTR(title, 1, 2)= 'Dr' THEN 'FOUND' ELSE 'NOPE' END as flag
FROM employees
of use a subselect eg:in join
SELECT title, t.nameprefix ,
CASE WHEN nameprefix = 'Dr' THEN 'FOUND' ELSE 'NOPE' END as flag
FROM employees
JOIN (select id, SUBSTR(title, 1, 2) as nameprefix
FROM employees ) t on t.id = employees.id

How to select different columns depending on values in a column

So, I have a table with the columns staff, associate and Matter type (which is always either set to 'Lit' or 'PSD'.)
When type field = 'Lit' I need to include the Staff field as the staff field in the select statement. When the type field is set to 'PSD' I need to include the associate field as the staff field in the select statement.
I know I can do this as two separate queries, but I cannot figure out how to combine the two into a single query - there's an easy answer, but after not being able to figure it out for a while, I'm just not coming up with the answer.
Ideas?
SELECT
CASE WHEN
[MatterType] = 'Lit'
THEN
[Staff]
ELSE
[Associate]
END AS [NewStaff]
FROM
MyTable;
This uses an inline case condition in the SELECT list.
To combine the results of two queries with same number of columns, you can use UNION ALL or UNION. Preferably union all because of less overhead.
SELECT staff AS staff ,
mattertype
FROM my_table
WHERE mattertype = 'Lit'
UNION ALL
SELECT associate AS staff ,
mattertype
FROM my_table
WHERE mattertype = 'PSD'
In your case, I would say using CASE is better:
SELECT CASE WHEN mattertype = 'Lit' THEN staff
ELSE associate
END AS staff
,mattertype
FROM my_table
If I understand your question right you want either staff or associate as a column called staff depending on the value of matter. If this is the cas you can use a conditional case ... when statement to select the appropriate columns. Something like this:
select matter, case when matter = 'Lit' then staff else associate end as staff from table
As you state that matter has to be either Lit or PSD you only need to check if it is one of the values, otherwise it has to be the other (although you could make the check explicit for clarity).
The other answers have covered the common & practical, so here is a variation which is sometimes useful. If your staff column is null when [Matter type] = 'PSD' then this would work:
SELECT COALESCE(staff,associate) AS staff
FROM tablename
;

SUM of a field within one table, GROUP BY a field in a second table

I found how to use SUM and GROUP BY within one table. What I try to do is to sum one field (Marks.m_GPA) in one table depending on a field in a second table (Curriculum.[c_Subject Group]).
I have one table with all the results of the students named Marks and another table named Curriculum. Both tables have the subject code in common (c_Code and m_Code), the Curriculum table has got one field [c_Subject Group]. I want to have the sum of all m_GPA values for each [c_Subject Group] and the results GROUP BY [c_Subject Group].
All fields are strings (text).
It's an ADO SQL VB6 application where I use the MS Jet engine of Access. I usually try to run a query using Access to test the query beforehand, ACCESS seems to accept my syntax, but the result is an empty table.
Here is the query:
SELECT
Curriculum.[c_Subject Group],
Sum([m_GPA]) AS Total_GPA
FROM Curriculum
INNER JOIN Marks ON
Curriculum.c_Code = Marks.m_Code
WHERE Curriculum.[c_Subject Group]= "1"
GROUP BY Curriculum.[c_Subject Group];
Even if I try Sum(cdbl(Marks.[m_GPA])), the result is an empty table.
Try this one.
SELECT
Curriculum.[c_Subject Group],
Marks.Total_GPA
FROM Curriculum
LEFT JOIN (
SELECT
m_Code,
Sum([m_GPA]) AS Total_GPA
FROM Marks
GROUP BY m_Code
)Marks
ON Curriculum.c_Code = Marks.m_Code
WHERE Curriculum.[c_Subject Group]= '1'

Aggregate/Group table rows in SQL if a column has a specific value

I am working with a rather hairy Stored Proc which returns XML (the XML is transformed with a stylesheet and printed out to a page; essentially a GridView in XML). I have a requirement to aggregate multiple rows (with the same ID, of course) into a single row only if the value of a particular column is not one of two specific values (in which case the columns are not aggregated).
In effect I need to do the following (in pseudocode):
select quoteID, customerName, shippingCharges, description, /*other columns*/
from quotes q
inner join customers c on q.customerID = c.customerID
where shipDate between #fromDate and #toDate
for xml explicit
if description != "Insurance" and description != "Pickup":
/*aggregate the rows for that quoteID into one single row
adding together charges and the like as needed*/
else:
/*nothing - okay to have multiple rows*/
How should I go about handling this type of logic? My first guess would be to put all the values into a temp table (or CTE) and then somehow check all of the data to see if I need to extract and combine rows but I'm drawing a blank as to how that actually is done, which is usually an indicator it's not the correct way of doing it...
Would this be better (and perhaps easier?) to do in the XSL transformation file instead? My choices are limited to three options:
Aggregate in the stored procedure, leave XSLT untouched
Aggregate in the XSLT, leave stored procedure untouched
This can't be achieved with the way the data is being returned currently (or can't be achieved without lots of time-consuming workarounds)
EDIT
One of the issues I'm facing is that the records will usually have the same ID field, and therefore the totals are coming out incorrect because I am summing the whole record (which calculates the total for the field I want and the field that I don't want).
For example a record might be something like:
1234 Insurance 54.65
1234 Shipping Charge 160.00
1234 Some Other Charge 15.00
and I would want the finished result to be like this:
1234 Shipment 175.00
1234 Insurance 54.65
what's happening is this:
1234 Shipment 229.65
1234 Insurance 229.65
and it's throwing the totals off.
The strategy I was using was to create a CTE called "AggregateData" that sums up the various amounts and groups by the ID and the Description; this fails because it gives the above result (sums for each description, so the value appears twice and is added twice on the report). The closest I have gotten is to NOT group it by the Description but instead wrapping it in the Max function (Yes, I know that's not a good idea). This gives me the correct totals but the description isn't accurately being reflected e.g. some records should be "Insurance" but are showing "Shipment" instead.
The easiest way would be to write two queries and union the results
select <columns>
where description not in('Insurance','Pickup')
group by <some columns)
union all
select <columns>
where description in('Insurance','Pickup')
Maybe something like this:
SELECT
customerName,
quoteID,
description = CASE
WHEN description IN ('Insurance', 'Pickup') THEN description
ELSE 'Shipment'
END,
shippingCharges = SUM(shippingCharges)
FROM quotes q
INNER JOIN customers c ON q.customerID = c.customerID
GROUP BY
customerName,
quoteID,
CASE
WHEN description IN ('Insurance', 'Pickup') THEN description
ELSE 'Shipment'
END
You must have noticed that the same CASE expression is repeated twice here. You can avoid it by using an ordinary subselect:
SELECT
customerName,
quoteID,
description,
shippingCharges = SUM(shippingCharges)
FROM (
SELECT
customerName,
quoteID,
description = CASE
WHEN description IN ('Insurance', 'Pickup') THEN description
ELSE 'Shipment'
END,
shippingCharges = SUM(shippingCharges)
FROM quotes q
INNER JOIN customers c ON q.customerID = c.customerID
) s
GROUP BY
customerName,
quoteID,
description
or a CTE:
WITH preparedList AS (
SELECT
customerName,
quoteID,
description = CASE
WHEN description IN ('Insurance', 'Pickup') THEN description
ELSE 'Shipment'
END,
shippingCharges = SUM(shippingCharges)
FROM quotes q
INNER JOIN customers c ON q.customerID = c.customerID
)
SELECT
customerName,
quoteID,
description,
shippingCharges = SUM(shippingCharges)
FROM preparedList
GROUP BY
customerName,
quoteID,
description
You might also want to add some other columns, as needed.