Restrict SQL subquery in SELECT - sql

I thought the subquery within the select statement will be restricted by the FROM and/or JOIN statements.
Therefore, my query always returns an error because there is more than one row in the subquery.
SELECT
dbo.Countries.Name,
dbo.Countries.ISO2,
(SELECT dbo.CountryFields.Field
FROM dbo.CountryFields
WHERE dbo.CountryFields.Field = 'Population') AS Population
FROM
dbo.CountryFields
INNER JOIN
dbo.Countries ON (dbo.CountryFields.Countries_Id = dbo.Countries.Countries_Id)
How can I restrict the number of rows in my subquery?
Do I need there also an inner join Statement inside the subquery? I hoped the subquery will inherit from normal SELECT so I don't need manual restrictions.
The column "Field" contains more than "Population" and I would like to show more rows in the SELECT statement with subselects but now ... I can't even get one column to work. :-(

I think you want something like this:
SELECT
a.Name,
a.ISO2,
(SELECT TOP 1 b.Field FROM dbo.CountryFields b WHERE b.Countries_Id = a.Countries_Id AND b.Field = 'Population') AS Population,
(SELECT TOP 1 b.Field FROM dbo.CountryFields b WHERE b.Countries_Id = a.Countries_Id AND b.Field = 'Capital') AS Capital,
(SELECT TOP 1 b.Field FROM dbo.CountryFields b WHERE b.Countries_Id = a.Countries_Id AND b.Field = 'Area') AS Area
FROM
dbo.Countries a
Of course there are ways to optimize the above query, but it's always a tradeoff between readability and speed.
Good luck!

I think that something like this is the proper query:
SELECT
C.Name,
C.ISO2,
ISNULL(CF_POP.Value,0) AS [Population],
ISNULL(CF_F2.Value,0) AS [Field2],
ISNULL(CF_F3.Value,0) AS [Field3]
FROM
dbo.Countries AS C
LEFT JOIN dbo.CountryFields AS CF_POP ON (C.Countries_Id = CF_POP.Countries_Id) AND (CF_POP.Field = 'Population')
LEFT JOIN dbo.CountryFields AS CF_F2 ON (C.Countries_Id = CF_F2.Countries_Id) AND (CF_F2.Field = 'Field2')
LEFT JOIN dbo.CountryFields AS CF_F3 ON (C.Countries_Id = CF_F3.Countries_Id) AND (CF_F3.Field = 'Field3')
In this example you connect each row from CountryFields as a column. I use LEFT JOIN, because I don't know how complete is your data (if you want to see blanks you have to remove ISNULL). I also put column Value, because I suppose that there must be second column which corresponds to CountryFields.Field. This also can be done with CROSS APPLY, but in that case syntax will be different.

Related

Subquery returning more than one result

I am still fairly new to SQL and the stored procedure I recently created keeps telling me that a subquery is returning more than one result but I can't figure out which one is the problem. If anyone has a moment and can tell me what I am missing, I would greatly appreciate it!
Thanks!
SELECT DISTINCT a.customer_no [id],
x.esal1_desc [constituent],
a.perf [activity],
a.sp_act_dt [activity_date],
c.description[activity_type],
d.display_name_tiny [solicitor],
s.description [status],
ISNULL(a.num_attendees,0)[attending],
a.notes [notes],
e.address [email]
FROM [dbo].t_special_activity a
left outer join [dbo].tr_special_activity_status s ON s.id = a.status
left outer join [dbo].tr_special_activity c ON c.id = a.sp_act
left outer JOIN [dbo].FT_CONSTITUENT_DISPLAY_NAME() d ON a.worker_customer_no = d.customer_no
left outer JOIN [dbo].T_EADDRESS e on a.customer_no=e.customer_no and primary_ind='Y'
left outer JOIN [dbo].TX_CUST_SAL x on a.customer_no=x.customer_no and default_ind='Y'
WHERE a.status IN (ISNULL(#status, (SELECT DISTINCT id FROM TR_SPECIAL_ACTIVITY_STATUS)))
AND a.sp_act_dt BETWEEN (ISNULL(#activity_start,(SELECT MIN(sp_act_dt) FROM T_SPECIAL_ACTIVITY)))
AND (ISNULL(#activity_end,(SELECT MAX(sp_act_dt) FROM T_SPECIAL_ACTIVITY)))
AND ((ISNULL(#list,0) = 0) OR EXISTS (SELECT customer_no FROM T_LIST_CONTENTS lc WITH (NOLOCK)
WHERE a.customer_no = lc.customer_no and lc.list_no = #list))
Alas, you cannot use this expression:
WHERE a.status IN (ISNULL(#status, (SELECT DISTINCT id FROM TR_SPECIAL_ACTIVITY_STATUS)))
The subquery is in a place where a single value is expected. In any case, I think you want:
WHERE #status IS NULL OR
a.status IN (SELECT id FROM TR_SPECIAL_ACTIVITY_STATUS)
Note that select distinct is irrelevant in an IN clause. At best it does nothing; at worst it impedes the optimizer.
I realize this is a little confusing. You are thinking that IN takes a list -- and the list could even be a subquery. But, the elements of the list are scalars not lists. So, when a subquery is an element of the list, then it is assumed to be a single value.

Need help in optimizing sql query

I am new to sql and have created the below sql to fetch the required results.However the query seems to take ages in running and is quite slow. It will be great if any help in optimization is provided.
Below is the sql query i am using:
SELECT
Date_trunc('week',a.pair_date) as pair_week,
a.used_code,
a.used_name,
b.line,
b.channel,
count(
case when b.sku = c.sku then used_code else null end
)
from
a
left join b on a.ma_number = b.ma_number
and (a.imei = b.set_id or a.imei = b.repair_imei
)
left join c on a.used_code = c.code
group by 1,2,3,4,5
I would rewrite the query as:
select Date_trunc('week',a.pair_date) as pair_week,
a.used_code, a.used_name, b.line, b.channel,
count(*) filter (where b.sku = c.sku)
from a left join
b
on a.ma_number = b.ma_number and
a.imei in ( b.set_id, b.repair_imei ) left join
c
on a.used_code = c.code
group by 1,2,3,4,5;
For this query, you want indexes on b(ma_number, set_id, repair_imei) and c(code, sku). However, this doesn't leave much scope for optimization.
There might be some other possibilities, depending on the tables. For instance, or/in in the on clause is usually a bad sign -- but it is unclear what your intention really is.

SQL - How to put a condition for which table is selected without left join

I have a flag in a table which value ( 1 for US, or 2 for Global) indicates if the data will be in Table A or Table B.
A solution that works is to left join both tables; however this slows down significantly the scripts (from less than a second to over 15 seconds).
Is there any other clever way to do this? an equivalent of
join TableA only if TableCore.CountryFlag = "US"
join TableB only if TableCore.CountryFlag = "global"
Thanks a lot for the help.
You can try using this approach:
-- US data
SELECT
YourColumns
FROM
TableCore
INNER JOIN TableA AS T ON TableCore.JoinColumn = T.JoinColumn
WHERE
TableCore.CountryFlag = 'US'
UNION ALL
-- Non-US Data
SELECT
YourColumns -- These columns must match in number and datatype with previous SELECT
FROM
TableCore
INNER JOIN TableB AS T ON TableCore.JoinColumn = T.JoinColumn
WHERE
TableCore.CountryFlag = 'global'
However, if the result is still slow, you might want to check if the TableCore table has a index on CountryFlag and JoinColumn, and TableA and TableB an index on JoinColumn.
The basic structure is:
select . . ., coalesce(a.?, b.?) as ?
from tablecore c left join
tablea a
on c.? = a.? and c.countryflag = 'US' left join
tableb b
on c.? b.? and c.counryflag = 'global';
This version of the query can take advantage of indexes on tablea(?) and tableb(?).
If you have a complex query, this portion is probably not responsible for the performance problem.

SQL: Check for 'value' is not null returns TRUE when 'value' is null

I am using SQL Server 2008, and when I perform a left join to a table where the outer table does not have any records then I am seeing weird behavior from my where clause. If I do a check for a value from my outer table being 'not null' it sometimes returns true.
select *
from foo f left join bar b on b.id=f.id
where f.id=#id and (f.status = 1 or b.Price is not null)
When my f.status = 0 and b.Price does not exist (or, appears as null in the select) this query selects records where f.status = 0 and b.Price is null, even though (FALSE OR FALSE) should be FALSE.
If I just perform this query, it works as expected and anything without a record in 'bar' does not get selected.
select *
from foo f left join bar b on b.id=f.id
where f.id=#id and b.Price is not null
Having b.Price is not null as part of an or operation seems to be causing issue for some reason. What could be wrong with this query? I run the same query with similar data on a SQL Server 2012 machine and do not see this issue, could it be related the the version of SQL Server I am using?
These two formulations are not the same, as you have discovered.
In the first query, price can be NULL for two reasons:
There is no match from the left join.
There is a match and b.Price is null
I highly recommend the second approach, putting the condition in the on clause. However, if you do use the first one, make the comparison to a column used in the join:
where f.id = #id and (f.status = 1 or b.id is not null)
You could try an OUTER APPLY like this:
SELECT *
FROM foo f
OUTER APPLY (
SELECT *
FROM bar b
WHERE f.id = b.id
AND (
f.STATUS = 1
OR b.Price IS NOT NULL
) b
)
WHERE f.id = #id
And I also suggest using the columns instead of *, bad practice. The Outer Apply is sort of like a left join and in this case it will filter all the data from the bar table and bring you back only the data you need.
Would a CASE statement work?
IE
SELECT
(etc etc code)
CASE WHEN b.Price is not null THEN 1 ELSE 0 END AS [MyBooleanCheck]
FROM (etc etc code)

Select Case value not sorting correctly in order by

I have a result set that has a defined grouping (for a report). There is a possibility that the position is not assigned and therefore does not have a "Grid_Group". In this case I assign a value of 99. This is working correctly except the order by, the 99 is always first and it should be last (If I do desc it is at bottom). I've tried a cast on the select side as well as within the order by on the Grid_Group, but both have same results with 99 at the top. (Sql server 2008)
Here is the snippet, I remove all other unneeded columns.
SELECT s.SessionNumber,Position.PositionName,(Select CASE when dbo.Position.Grid_Group is null THEN 99 ELSE dbo.Position.Grid_Group END) as Grid_Group
FROM dbo.USession AS us Left Outer JOIN
dbo.Position ON us.PositionId = dbo.Position.PositionId FULL OUTER JOIN
dbo.Sessions AS s ON us.SessionId = s.SessionId
ORDER BY S.SessionNumber, dbo.Position.Grid_Group
Thoughts?
You need to apply the CASE on your order by as well (bear in mind this will ruin index utilization on the sort operation). Your ORDER BY is referencing the original table's column, not your alias result column. Something like this should do the trick:
SELECT
s.SessionNumber,Position.PositionName,
(CASE
WHEN dbo.Position.Grid_Group IS NULL THEN 99
ELSE dbo.Position.Grid_Group
END) AS Grid_Group
FROM dbo.USession AS us
LEFT OUTER JOIN dbo.Position ON us.PositionId = dbo.Position.PositionId
FULL OUTER JOIN dbo.Sessions AS s ON us.SessionId = s.SessionId
ORDER BY
S.SessionNumber,
(CASE
WHEN dbo.Position.Grid_Group IS NULL THEN 99
ELSE dbo.Position.Grid_Group
END)
I allowed myself to apply some minor formatting of your SQL.
An ORDER BY will in this case not see your calculated columns. To get the effect you're asking for, you'll have to ORDER BY the same expression (which kastermester's answer is demonstrating), or wrap the query in a common table expression and ORDER BY while selecting from that, something like;
WITH cte AS (
SELECT s.SessionNumber, p.PositionName, COALESCE(p.Grid_Group, 99) Grid_Group
FROM dbo.USession AS us
LEFT OUTER JOIN dbo.Position p ON us.PositionId = p.PositionId
FULL OUTER JOIN dbo.Sessions s ON us.SessionId = s.SessionId
)
SELECT * FROM cte ORDER BY SessionNumber, Grid_Group;