SQL - How to return a value if nulls is returned - sql

I am trying to write a query for a parameter in SSRS where if it doesn't pull back any data, it will put "None" in the parameter. Here is what I have tried:
select CASE
WHEN pt.propertynumber IS NOT NULL
THEN pt.propertynumber
ELSE 'None'
END AS "Field1"
from projectmaintenanceproperties pmp
left join projectmaintenances pm on pm.id = pmp.projectmaintenanceid
left join properties pt on pt.id = pmp.propertyid
where pm.id in (:Ops)
and pt.projectid in ( :Proj )
order by 1
When there is no data pulled back, it is not returning anything in Field1.
How can I tell it to return 'None'?

Okay, here's a bit of a nightmare that might do the trick. I'm doing this nightmare because I once had a query that very cleverly used UNION and NOT EXISTS to return a completely different query and that resulted in a disastrous query plan when that second query executed.
What I'm doing below is using a fake LEFT OUTER JOIN with a table of only one row to your desired values table. It's fake because the join predicate is always true. So for every row from your query that will column from the ForNone query will be returned as well, but ignored since coalesce will return the value from your query (unless it is null, and if that is a possibility you can add a check in your query). If there are no results from your query then only one row is returned because and coalesce will use the value 'None'.
SELECT COALESCE(src.Field1, ForNone.NoneField) AS "Field1"
FROM ( SELECT 'None' AS "NoneField" ) ForNone
LEFT OUTER JOIN (
select pt.propertynumber AS "Field1"
from projectmaintenanceproperties pmp
left join projectmaintenances pm on pm.id = pmp.projectmaintenanceid
left join properties pt on pt.id = pmp.propertyid
where pm.id in (:Ops)
and pt.projectid in ( :Proj )
order by 1
) src ON 1 = 1

Oops, comments are correct - my bad. You'll instead need an IF statement to check if any values are returned:
IF EXISTS
( select CASE
WHEN pt.propertynumber IS NOT NULL
THEN pt.propertynumber
ELSE 'None'
END AS "Field1"
from projectmaintenanceproperties pmp
left join projectmaintenances pm on pm.id = pmp.projectmaintenanceid
left join properties pt on pt.id = pmp.propertyid
where pm.id in (:Ops)
and pt.projectid in ( :Proj )
order by 1;
)
THEN select pt.propertynumber [Field1]
from projectmaintenanceproperties pmp
left join projectmaintenances pm on pm.id = pmp.projectmaintenanceid
left join properties pt on pt.id = pmp.propertyid
where pm.id in (:Ops)
and pt.projectid in ( :Proj )
order by 1;
ELSE
SELECT 'None' AS [Field1];
END IF;

Related

Distinguishing which value is obtained from COALESCE by concatenating the value to a different prefix

SELECT COALESCE(grouped_wells.groupforecasting_id,wells.id) as the_id, string_agg(wells.name,', ') as well_name, sum(gas_cd) as gas_cd, date
FROM productions
INNER JOIN completions on completions.id = productions.completion_id
INNER JOIN wellbores on wellbores.id = completions.wellbore_id
INNER JOIN wells on wells.id = wellbores.well_id
INNER JOIN fields on fields.id = wells.field_id
INNER JOIN clusters on clusters.id = fields.cluster_id
LEFT JOIN grouped_wells on grouped_wells.wells_id = wells.id
LEFT JOIN groupforecasting on groupforecasting.id = grouped_wells.groupforecasting_id and groupforecasting.workspace_id = 3
GROUP BY the_id, productions.date
ORDER BY the_id, productions.date
In the above SQL, I am grouping by the_id that results from COALESCE. I want to avoid grouping grouped_wells.groupforecasting_id of equal value to wells.id.
One possible way is to add a prefix depending on which of the two value I got from COALESCE, either add "group_" or "well_".
How to add this conditional prefix to the_id?
If grouped_wells.groupforecasting_id is Null then concatenate "well_" to the result of COALESCE.
Else concatenate "group_" to the result of COALESCE.
You nearly had the solution in your
If grouped_wells.groupforecasting_id is Null then concatenate "well_"
to the result of COALESCE. Else concatenate "group_" to the result of
COALESCE.
In postgres you can use the case when syntax:
case when ... then ...
else ...
end as the_id
completing it in your select (and your comment):
SELECT
case when grouped_wells.groupforecasting_id is NULL then concat('well_', wells.id)
else
concat('group_', grouped_wells.groupforecasting_id)
end as the_id,
string_agg(wells.name,', ') as well_name,
sum(gas_cd) as gas_cd, date
FROM productions
INNER JOIN completions on completions.id = productions.completion_id
INNER JOIN wellbores on wellbores.id = completions.wellbore_id
INNER JOIN wells on wells.id = wellbores.well_id
INNER JOIN fields on fields.id = wells.field_id
INNER JOIN clusters on clusters.id = fields.cluster_id
LEFT JOIN grouped_wells on grouped_wells.wells_id = wells.id
LEFT JOIN groupforecasting on groupforecasting.id = grouped_wells.groupforecasting_id and groupforecasting.workspace_id = 3
GROUP BY the_id, productions.date
ORDER BY the_id, productions.date

Query runs very slow with big tables

Having the following 2 tables:
Table MainProcessed:
Id Value
---------
1 123
2 234
3 112
Table MainAdditionalInfo:
Id MainProcessedId Name Value
--------------------------------------
1 1 'PX' 'px_value'
2 1 'PY' 'py_value'
I need to select all the data from the table MainProcessedId that has some additional info in the table MainAdditionalInfo (at least one record) and it doesn't have a record with the name PX or if it has, this value should be null or empty.
This is what I've tried, but since these tables have a lot of data (more that 100 millions of records, the query is running for a lot of time):
select mp.*
from MainProcessed mp (nolock)
left join MainAdditionalInfo mai1 (nolock) on mp.Id = mai1.MainProcessedId
left join MainAdditionalInfo mai2 (nolock) on mp.Id = mai2.MainProcessedId
where
(mai1.Value is null or mai1.Value = '')
and (mai1.Name = 'PX' or mai1.Name = null)
and mai2.name = 'PY'
Note that the value with the name PX might not be present or can be present with a null or empty value, but the value with the name PY it's always present. Can you suggest me an improving?
Also, I don't have the rights to see the execution plans or to create new objects (indexes).
You could try and phrase your query with exists and not exists:
select p.*
from MainProcessed p
where
exists (
select 1
from MainAdditionalInfo a
where a.MainProcessedId = p.id)
and not exists (
select 1
from MainAdditionalInfo a
where
a.MainProcessedId = p.id
and a.Name = 'PX'
and a.Value <> '' -- null values won't pass that test
)
For performance with this query, you want an index on MainAdditionalInfo(MainProcessedId , Name, Value).
select mp.*
from MainProcessed mp
left join MainAdditionalInfo mai1 on mp.Id = mai1.MainProcessedId and mai2.name ='PX'
where Id in(
select distinct MainProcessedId
from MainAdditionalInfo mai2
inner join MainProcessed on Id = mai2.MainProcessedId and mai2.name ='PY'
)
and (mai1.Value is null or mai1.Value = '')
This is too long for a comment, but I'm expanding on my second one. This doesn't, also, answer the question (there's not enough detail in the question to answer it), but it might help the query planner anyway.
Firstly, you have 2 clauses in you WHERE, like (mai1.Value is null or mai1.Value = '') I assume this is because Value might be NULL if the JOIN fails. If so, don't handle it in your WHERE, put it in the ON.
You also have and mai2.name = 'PY', however, you perform a LEFT JOIN toMainAdditionalInfo mai2. This means the JOIN is now an implicit INNER JOIN. I would, therefore, personally write your query as:
SELECT {List columns, do not use *}
FROM MainProcessed mp
JOIN MainAdditionalInfo mai2 ON mp.Id = mai2.MainProcessedId
LEFT JOIN MainAdditionalInfo mai1 ON mp.Id = mai1.MainProcessedId
AND mai1.Value = ''
AND mai1.Name = 'PX'
WHERE mai2.[name] = 'PY';
I've also removed the NOLOCK hints, as I suspect they do far more harm than good. Bad habits : Putting NOLOCK everywhere
At first, you should ensure that you have FK on MainAdditionalInfo to MainProcessed.ID with index on MainAdditionalInfo.MainProcessedId. Also, you should have an index on MainAdditionalInfo at columns name and value.
Don't use join if you don't need data from MainAdditionalInfo, use exists, for example:
SELECT mp.*
FROM MainProcessed mp (nolock)
WHERE NOT EXISTS (SELECT 1 FROM MainAdditionalInfo mai1
WHERE mai1.Value > ''
AND mai1.Name = 'PX'
AND mp.Id = mai1.MainProcessedId)
AND EXISTS (SELECT 1 FROM MainAdditionalInfo mai2
WHERE mai2.name = 'PY'
AND mp.Id = mai2.MainProcessedId)

SQL SELECT Statement Datetime Troubles with Inner Joins

I have this query that returns no records because its can't match the grp.EffectiveDate specified while their are Inner Joins within the query. Database is SQL Server.
SELECT grp.GroupID, grp.GroupNumber, grp.Name, grp.Location, grp.GroupTypeID, grp.DivisionID,
grp.MasterGroupID, grp.EffectiveDate, grp.TerminationDate, crt.[ContractNumber], pln.[PBPNumber], div.SiteName, src.Name as SourceName
FROM [Group] grp
INNER JOIN [IndividualPlanDemographic] idp ON grp.GroupID = idp.IndividualPlanDemographicID
INNER JOIN [Plan] pln ON idp.PlanID = pln.PlanID
INNER JOIN [Contract] crt ON pln.ContractID = crt.ContractID
INNER JOIN [Division] div ON grp.DivisionID = div.DivisionID
INNER JOIN [SourceSystem] src ON div.SourceSystemID = src.SourceSystemID
WHERE 1 = 1
AND grp.EffectiveDate = '1/1/2015 12:00:00 AM' AND grp.GroupTypeID = '2' ORDER BY ContractNumber
However, if I just query the main "Group" table, it will return the correct records I'm looking for based on all the criteria.
SELECT grp.GroupID, grp.GroupNumber, grp.Name, grp.Location, grp.GroupTypeID, grp.DivisionID,
grp.MasterGroupID, grp.EffectiveDate, grp.TerminationDate
FROM [Group] grp
WHERE 1 = 1
AND grp.EffectiveDate = '1/1/2015 12:00:00 AM' AND grp.GroupTypeID = '2'
Why will my query not working when using more than one table? I specifically reference the table alias before the column (grp.EffectiveDate) so I don't understand what else is wrong. As always, thank you in advance for your help.
Perhaps one of your INNER JOIN's should be a LEFT OUTER JOIN. When using joins and you want parent records to be returned with/without children records and this is allowed by your schema (nullable FK) then you should left outer join.
SELECT grp.GroupID, grp.GroupNumber, grp.Name, grp.Location, grp.GroupTypeID, grp.DivisionID,
grp.MasterGroupID, grp.EffectiveDate, grp.TerminationDate, crt.[ContractNumber], pln.[PBPNumber], div.SiteName, src.Name as SourceName
FROM [Group] grp
INNER JOIN [IndividualPlanDemographic] idp ON grp.GroupID = idp.IndividualPlanDemographicID
INNER JOIN [Plan] pln ON idp.PlanID = pln.PlanID
INNER JOIN [Contract] crt ON pln.ContractID = crt.ContractID
--THE GROUP IS NOT REQUIRED TO BE IN A DIVISION RELATIONSHIP
LEFT OUTER JOIN [Division] div ON grp.DivisionID = div.DivisionID
LEFT OUTER JOIN [SourceSystem] src ON div.SourceSystemID = src.SourceSystemID
WHERE 1 = 1
AND grp.EffectiveDate = '1/1/2015 12:00:00 AM' AND grp.GroupTypeID = '2' ORDER BY ContractNumber
Ultimately this means that the rows that match this date and group type do not contain records in all the tables that you are INNER JOINed to.
Either your database is missing expected records, or you need to change some of these INNER JOINs to LEFT OUTER JOINs instead.

SQL query join conditions

I have a query (exert from a stored procedure) that looks something like this:
SELECT S.name
INTO #TempA
from tbl_Student S
INNER JOIN tbl_StudentHSHistory TSHSH on TSHSH.STUD_PK=S.STUD_PK
INNER JOIN tbl_CODETAILS C
on C.CODE_DETL_PK=S.GID
WHERE TSHSH.Begin_date < #BegDate
Here is the issue, the 2nd inner join and corresponding where statement should only happen if only a certain variable (#UseArchive) is true, I don't want it to happen if it is false. Also, in TSHSH certain rows might have no corresponding entries in S. I tried splitting it into 2 separate queries based on #UseArchive but studio refuses to compile that because of the INTO #TempA statement saying that there is already an object named #TempA in the database. Can anyone tell me of a way to fix the query or a way to split the queries with the INTO #TempA statement?
Looks like you're asking 2 questions here.
1- How to fix the SELECT INTO issue:
SELECT INTO only works if the target table does not exist. You need to use INSERT INTO...SELECT if the table already exists.
2- Conditional JOIN:
You'll need to do a LEFT JOIN if the corresponding row may not exist. Try this.
SELECT S.name
FROM tbl_Student S
INNER JOIN tbl_StudentHSHistory TSHSH
ON TSHSH.STUD_PK=S.STUD_PK
LEFT JOIN tbl_CODETAILS C
ON C.CODE_DETL_PK=S.GID
WHERE TSHSH.Begin_date < #BegDate
AND CASE WHEN #UseArchive = 1 THEN c.CODE_DETL_PK ELSE 0 END =
CASE WHEN #UseArchive = 1 THEN S.GID ELSE 0 END
Putting the CASE statement in the WHERE clause and not the JOIN clause will force it to act like an INNER JOIN when #UseArchive and a LEFT JOIN when not.
I'd replace it with LEFT JOIN
LEFT JOIN tbl_CODETAILS C ON #UseArchive = 1 AND C.CODE_DETL_PK=S.GID
You can split the queries and then insert into a temp table easily.
SELECT * INTO #TempA FROM
(
SELECT * FROM Q1
UNION ALL
SELECT * FROM Q2
) T
SELECT S.name
INTO #TempA
from tbl_Student S
INNER JOIN tbl_StudentHSHistory TSHSH
on TSHSH.STUD_PK = S.STUD_PK
INNER JOIN tbl_CODETAILS C
on C.CODE_DETL_PK = S.GID
and #UseArchive = true
WHERE TSHSH.Begin_date < #BegDate
But putting #UseArchive = true in the join in this case is the same as where
Your question does not make much sense to me
So what if TSHSH certain rows might have no corresponding entries in S?
If you want just one of the joins to match
SELECT S.name
INTO #TempA
from tbl_Student S
LEFT OUTER JOIN tbl_StudentHSHistory TSHSH
on TSHSH.STUD_PK = S.STUD_PK
LEFT OUTER JJOIN tbl_CODETAILS C
on C.CODE_DETL_PK = S.GID
and #UseArchive = true
WHERE TSHSH.Begin_date < #BegDate
and ( TSHSH.STUD_PK is not null or C.CODE_DETL_PK id not null )

How to write subquery inside the OUTER JOIN Statement

I want to join two table CUSTMR and DEPRMNT.
My needed is: LEFT OUTER JOIN OF two or more Tables with subquery inside the LEFT OUTER JOIN as shown below:
Table: CUSTMR , DEPRMNT
Query as:
SELECT
cs.CUSID
,dp.DEPID
FROM
CUSTMR cs
LEFT OUTER JOIN (
SELECT
dp.DEPID
,dp.DEPNAME
FROM
DEPRMNT dp
WHERE
dp.DEPADDRESS = 'TOKYO'
)
ON (
dp.DEPID = cs.CUSID
AND cs.CUSTNAME = dp.DEPNAME
)
WHERE
cs.CUSID != ''
Here the subquery is:
SELECT
dp.DEPID, dp.DEPNAME
FROM
DEPRMNT dp
WHERE
dp.DEPADDRESS = 'TOKYO'
Is it possible to write such subquery inside LEFT OUTER JOIN?
I am getting an error when running this query on my DB2 database.
You need the "correlation id" (the "AS SS" thingy) on the sub-select to reference the fields in the "ON" condition. The id's assigned inside the sub select are not usable in the join.
SELECT
cs.CUSID
,dp.DEPID
FROM
CUSTMR cs
LEFT OUTER JOIN (
SELECT
DEPID
,DEPNAME
FROM
DEPRMNT
WHERE
dp.DEPADDRESS = 'TOKYO'
) ss
ON (
ss.DEPID = cs.CUSID
AND ss.DEPNAME = cs.CUSTNAME
)
WHERE
cs.CUSID != ''
I think you don't have to use sub query in this scenario.You can directly left outer join the DEPRMNT table .
While using Left Outer Join ,don't use columns in the RHS table of the join in the where condition, you ll get wrong output