How to select different columns depending on values in a column - sql

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
;

Related

How to do text calculations using columns in SQL

I'd like to conditionally include the values from particular columns in a SQL query. To illustrate the question let me use the fictional example of returning selective data about users.
If we were to return all data we'd use a query of the form:
SELECT
name, email, gender
FROM
users
Assume all users have entered their gender. However some have set the value of gender_public to be false and I would like to reflect this in the data the query returns.
The type of thing I'd like to do is:
SELECT
name, email, if(gender_public, gender, 'N/A')
FROM
users
Is this possible? I'm working with Postgres
Using CASE
This is the obvious way to go, as suggested by #a_horse_with_no_name.
Assuming gender_public is of type BOOLEAN.
SELECT name, email, CASE WHEN gender_public THEN gender ELSE 'N/A' END AS gender
FROM users;
Using lateral join
This way of doing is especially useful if you need to use the value in more than one place and want to remain consistent.
SELECT name, email, T.gender
FROM users
CROSS JOIN LATERAL (
SELECT gender WHERE show_gender
UNION
SELECT 'N/A' WHERE NOT(show_gender)
) T(gender)

SQL create columns with sum of another column by unique items

I am a little bit new to SQL. So hope you can help me with this question as I didnt find answer yet.
I have joined tables from other sources and I need to create new columns based on unique values of one column with summ of another col. values.
For example, this is my table:
And this is what I need to get (Summ of A for each unique value in B added as a column):
For now I can do it manually like this:
SELECT
EM.[id]
,SUM(CASE WHEN AM.[B]='ZA' THEN EM.[A] ELSE 0 END) "ZA_sum_A"
,SUM(CASE WHEN AM.[B]='ZB' THEN EM.[A] ELSE 0 END) "ZB_sum_A"
FROM [xxx].x.x AS AM
INNER JOIN [yyy].[y].[y] AS EM ON (AM.ELEMENT = EM.ELEMENT)
WHERE ...
GROUP BY EM.[id]
But issue is that I can have hundreds of unique values in B.
So, question how to do it correctly..
If you just want to see the summed values of EM.[A] for each combination of EM.[id] and AM.[B], you can do:
select
em.[id],
am.[B],
sum(em.[A])
from [xxx].x.x as am
join [yyy].[y].[y] as em on am.ELEMENT = em.ELEMENT
where [...]
group by em.[id], am.[B]
If you then want to have the distinct values of AM.[B] appear as columns, so there is only one row for each distinct value of EM.[id], you either need to know what the distinct values of AM.[B] are and use PIVOT.
If the exact values of AM.[B] are not known, or change often over time, you'll need to do something dynamic, like in this answer, but in the opposite direction.

How do I sum a column based on two conditions, one based on field value, the other based upon retrieved record's values?

I have a list like the one below from which I'm looking to aggregate the sum in the "amount" column for a given company. The trick of the matter is that I want to include family members of employees of the company. Those relations are kept by the ID to the right and will differ by the 12th character (if the family in question only has one member, then the 12th character is a space).
My question is, what is the most efficient way to get the amount for all employees of ABC Inc, including family members. I believe that this will require first one query for all employees of ABC Inc, then another for their family members by using the resulting list from query one.
Is this the most efficient way to do this? My table is extremely large (over 10GB of flat data), and thousands of such queries will be required, so efficiency is important.
The code I'm using thus far to get the data without family members is:
select ID, Name, Company_Name, sum(Amount) from indivs
where Orgname ='APC Inc' --or Employer like '%APC Inc%'
group by ID, Name, Company_Name
However, this only gives me the amounts from the direct employees.
What would be the next step to add the amounts for family members?
I think you want:
select sum(amount)
from t
where exists (select 1
from t t2
where t2.company = 'APC Inc.' and
left(t2.id, 11) = left(t.id, 11)
);
For performance, you can create a computed column and index:
alter table t add id11 as (left(id, 11)) persisted;
create index idx_company_id11 on t(company, id11);
Then phrase the query as:
select sum(amount)
from t
where exists (select 1
from t t2
where t2.company = 'APC Inc.' and
t2.id11 = t.id11
);

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

Dynamic Aliases in the SQL statement

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.