Selecting Data from a column where NULLs and strings exist - sql-server

Hi i have a query that appears to be pulling in 'Not Set' twice:
Now after a few checks i know it is because the currentstage column has NULLS and the string 'Not Set'stored in the table. So below yields 3 strings of 'Not set' and 195 NULLS forced to 'Not Set'.
But what i actually want to see is 198 x 'Not Sets' in my #TempCircCount. How can this be done please?
My Failing code is here:
IF OBJECT_ID('tempdb..#TempCircCount') is not null
DROP TABLE #TempCircCount
SELECT
ISNULL(cirRep.CurrentStage, 'Not Set') AS CurrentStage,
COUNT(ISNULL(cirRep.CurrentStage, 'Not Set')) AS Circuits
INTO #TempCircCount
FROM
[QuoteBase].[dbo].[CircuitReports] cirRep
RIGHT JOIN
Quotebase.dbo.Circuits cir ON cir.[PW Number] = CirRep.[PWNumber]
WHERE
Cir.Status='New Circuit Order'
GROUP BY CurrentStage
ORDER BY CurrentStage
SELECT
ISNULL(CurrentStage, 'Not Set') AS [CurrentStage],
Circuits AS Circuits
FROM #TempCircCount
GROUP BY CurrentStage, Circuits
ORDER BY CurrentStage

I believe simply changing
GROUP BY CurrentStage
to
GROUP BY ISNULL(cirRep.CurrentStage, 'Not Set')
would work.
The GROUP BY uses CurrentStage from one of your table fields (i.e. cirRep.CurrentStage) instead of the field in the select. SQL server disallows grouping by a field in the select.
I also recommend not using the same names for your output fields as already existing fields for exactly this reason.

Related

Weird result with MAX and COALESCE in a SQL query when run by different users

I have a SQL Server query that tries to choose NULL if there is at least one NULL in column PredComplDate or the latest date stored as NVARCHAR(30)
The weird thing is that when I run the query it returns correct results, but when my co-worker runs the query on the same computer it returns different results.
I saved the table created when my co-worker ran the query under a different name and compared it to the one that was created when I run the query. everything is the same, number of records, data type etc, but the below part of the query does not return NULL for some reason:
SELECT [Reference Number],
CASE
WHEN LEN(Predecessors)>0 AND MAX(COALESCE(PredComplDate,'31/12/2099'))='31/12/2099' THEN NULL
ELSE MAX(PredComplDate)
END AS LatestPredComplDate
FROM mytable33
GROUP BY [Reference Number],Predecessors
what is the problem with this query?
mytable33 has been created by me and mytable22 has been created by my co-worker. when I query the results are identical:
SELECT Predecessors,[Reference Number], PredRefNo,PredComplDate FROM mytable22
SELECT Predecessors,[Reference Number], PredRefNo,PredComplDate FROM mytable33
when I run the above-mentioned query on both tables, the result is different:
SELECT [Reference Number],
CASE
WHEN LEN(Predecessors)>0 AND MAX(COALESCE(PredComplDate,'31/12/2099'))='31/12/2099' THEN NULL
ELSE MAX(PredComplDate)
END AS LatestPredComplDate
FROM mytable22
GROUP BY [Reference Number],Predecessors
SELECT [Reference Number],
CASE
WHEN LEN(Predecessors)>0 AND MAX(COALESCE(PredComplDate,'31/12/2099'))='31/12/2099' THEN NULL
ELSE MAX(PredComplDate)
END AS LatestPredComplDate
FROM mytable33
GROUP BY [Reference Number],Predecessors
The expected result is NULL for LastestPredComplDate
Perhaps you can place both clauses in your windowed function like below (if only for clarity sakes):
MAX(CASE WHEN LEN(Predecessors)>0 THEN NULL ELSE PredComplDate END) AS LatestPredComplDate
Eventually, I found out the problem. The date (stored as NVARCHAR) format was the culprit. If you have another look at the first image that I posted you can see that the same dates have different formats. One is 04/12/2019 and the other is 4/12/2019. It turns out the MAX function would consider 4/12/2019 larger than 04/12/2019 and that would generate different results.

SSRS avoid WHERE clause if select all is selected

I am working on an SSRS report and a part of my sql query is like
WHERE SuperVisorId IN (#SupervisorIDs) AND CreatedDate> #StartDate
where the #SupervisorIDs is a dropdown with option of "select all" and individual supervisors.
So if the supervisors "all" option is selected , then I don't need to include that in the where clause and my where clause is only this
WHERE CreatedDate> #StartDate
So how can I make the WHERE clause looks different according to Selection of dropdown?
This only applies if you are using a single valued parameter with a manually added All option to the list of available values. Multi-value parameters do not know when all options are selected.
SQL Server doesn't always execute the conditions in a where clause in the order you write them, so if you are using where (#p = 'all' or col = #p) and ... you may still be comparing your values.
If performance is a concern, you can avoid this by using a short circuiting case, that only progresses to the actual data comparison if it is necessary:
where case when #SupervisorIDs = 'All' then 1
else case when SuperVisorId = #SupervisorIDs then 1
else 0
end
end = 1
and CreatedDate > #StartDate
Assuming that you are using a dataset query to populate the supervisor parameter dropdown, then you can try this.
Create an additional hidden parameter of a boolean type. For this example, I'll call it #AllSupsSelected. Set the default value of the parameter to:
=COUNT(Parameters!SupervisorIds.Label)=COUNT(Fields!SupervisorIdLabel.Value,"SupervisorDataset")
Replace the field and dataset names accordingly. If the dataset is returning non-distinct values, you may have to tinker further to get this working.
Now your query can read:
WHERE #AllSupsSelected OR SupervisorId IN (#SupervisorIds)
Make your where clause like below
WHERE (
(SuperVisorId IN (#SupervisorIDs))
OR (
#SupervisorIDs = 0
AND COLUMN_WITH_NULL IS NOT NULL
)
)
AND CreatedDate > #StartDate
And pass 0 when selected "select all"
As an actual answer to your particular problem, set your multi-valued parameter dataset up similar to this to return all Supervisors as well as a value at the bottom of the list for No Supervisor:
select distinct SupervisorID as Value
,SupervisorName as Label
,1 as Sort
from Suppliers
union all
select <Uniquely identifiable value with the same data type as SupervisorID> as Value
,'No Supervisor' as Label
,2 as Sort
order by Sort
,Label
Then in your dataset set up your filtering similar to the below. I have structured it in this manner to avoid using the isnull function on your SupervisorID column, which will hurt the query performance:
select cols
from tables
where SupervisorID in(#SupervisorID)
or (SupervisorID is null
and <Unique value from above> in (#SupervisorID)
)
which version of ssrs ? in 2016, you don't need to alter your query. when you click "select all" by default it pass all the values. so your query works good without changing anything.
thanks,
SK

Update a view doesn't work

I'm working on a view which is then updated by the user. This update basically changes the value of column. But right now it doesnt let me do that and produces this :
Update or insert of view or function '' failed because it contains a derived or constant field.
I know this is because I have a constant in the select statement but is there a way to get around it? Please help
This is my code for the view
Create view Schema.View1
as
SELECT
Convert(Varchar(20),l.jtpName) as JobType, Convert(Varchar(10),' <All> ')as SubCategory , Convert(varchar (3), Case when a.jtpName= l.jtpName and a.subName= ' <All> ' then 'Yes' else 'No' end) As AutoProcess from Schema.JobType l left join Schema.Table1 a on l.jtpName=a.jtpName
UNION
SELECT
Convert(Varchar(20),a.jtpName) as JobType, Convert(Varchar(10),a.subName) as SubCategory, Convert(varchar (3),Case when b.jtpName= a.jtpName and b.subName= a.subName then 'Yes' else 'No' end) As AutoProcess from Schema.SubCategory a left join fds.Table1 b on a.subName=b.subName
GO
Finally the update statement:
UPDATE Schema.View1 SET AUTOPROCESS = Case WHEN AUTOPROCESS = 'Yes' Then 'No' END Where JOBTYPE = 'Transport' and SUBCATEGORY= 'Cargo'
Thank You
You cannot update a column that is the result of a computation.
According to MSDN, one of the conditions for a view column to be updatable is this:
Any modifications, including UPDATE, INSERT, and DELETE statements, must reference columns from only one base table.
The columns being modified in the view must directly reference the underlying data in the table columns. The columns cannot be derived in any other way, such as through the following:
An aggregate function: AVG, COUNT, SUM, MIN, MAX, GROUPING, STDEV, STDEVP, VAR, and VARP.
A computation. The column cannot be computed from an expression that uses other columns. Columns that are formed by using the set operators UNION, UNION ALL, CROSSJOIN, EXCEPT, and INTERSECT amount to a computation and are also not updatable.
The columns being modified are not affected by GROUP BY, HAVING, or DISTINCT clauses.
TOP is not used anywhere in the select_statement of the view together with the WITH CHECK OPTION clause.
Here not only does your view uses the UNION statement, the AutoProcess field you are trying to update is actually the result of a CASE statement that uses two fields. It makes no sense to try and update that.
I would recommend that you use stored proc to perform writing operations. Or, as Damien suggest, you could use an INSTEAD OF trigger on the view too.
You have to create a TRIGGER and manually apply the changes from the inserted and deleted pseudo-tables against the base tables yourself.
There is no way for sql server to work backwards from your convert functions to the original fields. You cannot update a view this way.
If the view contained your jptName and subName fields, you might be able to update just those fields.

SQL Server ORDER BY date and nulls last

I am trying to order by date. I want the most recent dates coming in first. That's easy enough, but there are many records that are null and those come before any records that have a date.
I have tried a few things with no success:
ORDER BY ISNULL(Next_Contact_Date, 0)
ORDER BY ISNULL(Next_Contact_Date, 999999999)
ORDER BY coalesce(Next_Contact_Date, 99/99/9999)
How can I order by date and have the nulls come in last? The data type is smalldatetime.
smalldatetime has range up to June 6, 2079 so you can use
ORDER BY ISNULL(Next_Contact_Date, '2079-06-05T23:59:00')
If no legitimate records will have that date.
If this is not an assumption you fancy relying on a more robust option is sorting on two columns.
ORDER BY CASE WHEN Next_Contact_Date IS NULL THEN 1 ELSE 0 END, Next_Contact_Date
Both of the above suggestions are not able to use an index to avoid a sort however and give similar looking plans.
One other possibility if such an index exists is
SELECT 1 AS Grp, Next_Contact_Date
FROM T
WHERE Next_Contact_Date IS NOT NULL
UNION ALL
SELECT 2 AS Grp, Next_Contact_Date
FROM T
WHERE Next_Contact_Date IS NULL
ORDER BY Grp, Next_Contact_Date
According to Itzik Ben-Gan, author of T-SQL Fundamentals for MS SQL Server 2012, "By default, SQL Server sorts NULL marks before non-NULL values. To get NULL marks to sort last, you can use a CASE expression that returns 1 when the" Next_Contact_Date column is NULL, "and 0 when it is not NULL. Non-NULL marks get 0 back from the expression; therefore, they sort before NULL marks (which get 1). This CASE expression is used as the first sort column." The Next_Contact_Date column "should be specified as the second sort column. This way, non-NULL marks sort correctly among themselves." Here is the solution query for your example for MS SQL Server 2012 (and SQL Server 2014):
ORDER BY
CASE
WHEN Next_Contact_Date IS NULL THEN 1
ELSE 0
END, Next_Contact_Date;
Equivalent code using IIF syntax:
ORDER BY
IIF(Next_Contact_Date IS NULL, 1, 0),
Next_Contact_Date;
order by -cast([Next_Contact_Date] as bigint) desc
If your SQL doesn't support NULLS FIRST or NULLS LAST, the simplest way to do this is to use the value IS NULL expression:
ORDER BY Next_Contact_Date IS NULL, Next_Contact_Date
to put the nulls at the end (NULLS LAST) or
ORDER BY Next_Contact_Date IS NOT NULL, Next_Contact_Date
to put the nulls at the front. This doesn't require knowing the type of the column and is easier to read than the CASE expression.
EDIT: Alas, while this works in other SQL implementations like PostgreSQL and MySQL, it doesn't work in MS SQL Server. I didn't have a SQL Server to test against and relied on Microsoft's documentation and testing with other SQL implementations. According to Microsoft, value IS NULL is an expression that should be usable just like any other expression. And ORDER BY is supposed to take expressions just like any other statement that takes an expression. But it doesn't actually work.
The best solution for SQL Server therefore appears to be the CASE expression.
A bit late, but maybe someone finds it useful.
For me, ISNULL was out of question due to the table scan. UNION ALL would need me to repeat a complex query, and due to me selecting only the TOP X it would not have been very efficient.
If you are able to change the table design, you can:
Add another field, just for sorting, such as Next_Contact_Date_Sort.
Create a trigger that fills that field with a large (or small) value, depending on what you need:
CREATE TRIGGER FILL_SORTABLE_DATE ON YOUR_TABLE AFTER INSERT,UPDATE AS
BEGIN
SET NOCOUNT ON;
IF (update(Next_Contact_Date)) BEGIN
UPDATE YOUR_TABLE SET Next_Contact_Date_Sort=IIF(YOUR_TABLE.Next_Contact_Date IS NULL, 99/99/9999, YOUR_TABLE.Next_Contact_Date_Sort) FROM inserted i WHERE YOUR_TABLE.key1=i.key1 AND YOUR_TABLE.key2=i.key2
END
END
Use desc and multiply by -1 if necessary. Example for ascending int ordering with nulls last:
select *
from
(select null v union all select 1 v union all select 2 v) t
order by -t.v desc
I know this is old but this is what worked for me
Order by Isnull(Date,'12/31/9999')
I think I found a way to show nulls in the end and still be able to use indexes for sorting.
The idea is super simple - create a calculatable column which will be based on existing column, and put an index on it.
ALTER TABLE dbo.Users
ADD [FirstNameNullLast]
AS (case when [FirstName] IS NOT NULL AND (ltrim(rtrim([FirstName]))<>N'' OR [FirstName] IS NULL) then [FirstName] else N'ZZZZZZZZZZ' end) PERSISTED
So, we are creating a persisted calculatable column in the SQL, in that column all blank and null values will be replaced by 'ZZZZZZZZ', this will mean, that if we will try to sort based on that column, we will see all the null or blank values in the end.
Now we can use it in our new index.
Like this:
CREATE NONCLUSTERED INDEX [IX_Users_FirstNameNullLast] ON [dbo].[Users]
(
[FirstNameNullLast] ASC
)
So, this is an ordinary nonclustered index. We can change it however we want, i.e. include extra columns, increase number of indexes columns, change sorting order etc.
I know this is a old thread, but in SQL Server nulls are always lower than non-null values. So it's only necessary to order by Desc
In your case Order by Next_Contact_Date Desc should be enough.
Source: order by with nulls- LearnSql

How to select true/false based on column value?

I have a table with the following columns:
EntityId, EntityName, EntityProfile, .................
I want to select the Id and Name and true/false column based on the value of entity profile,
for example a returned result set like below, would mean that entities 1&2 have profiles while 3 not.
1 Name1 True
2 Name2 True
3 Name3 False
etc.....
I know I can do it using a function that return true/false based on the profile value like this:
SELECT EntityId, EntityName, dbo.EntityHasProfile(EntityId) AS HasProfile FROM Entities
but I'm returning a large no. of records and with this function call for each record, the query is very slow, and when I remove the function call the query execution time drops significantly.
So is there another way of doing this?
Thanks
Use a CASE. I would post the specific code, but need more information than is supplied in the post - such as the data type of EntityProfile and what is usually stored in it. Something like:
CASE WHEN EntityProfile IS NULL THEN 'False' ELSE 'True' END
Edit - the entire SELECT statement, as per the info in the comments:
SELECT EntityID, EntityName,
CASE WHEN EntityProfile IS NULL THEN 'False' ELSE 'True' END AS HasProfile
FROM Entity
No LEFT JOIN necessary in this case...
You can try something like
SELECT e.EntityId,
e.EntityName,
CASE
WHEN ep.EntityId IS NULL THEN 'False'
ELSE 'TRUE'
END AS HasProfile
FROM Entities e LEFT JOIN
EntityProfiles ep ON e.EntityID = ep.EntityID
Or
SELECT e.EntityId,
e.EntityName,
CASE
WHEN e.EntityProfile IS NULL THEN 'False'
ELSE 'TRUE'
END AS HasProfile
FROM Entities e
Maybe too late, but I'd cast 0/1 as bit to make the datatype eventually becomes True/False when consumed by .NET framework:
SELECT EntityId,
EntityName,
CASE
WHEN EntityProfileIs IS NULL
THEN CAST(0 as bit)
ELSE CAST(1 as bit) END AS HasProfile
FROM Entities
LEFT JOIN EntityProfiles ON EntityProfiles.EntityId = Entities.EntityId`
If the way you determine whether or not an entity has a profile is a deterministic function, and doesn't require any access to another table, you could write a stored function and define a computed, persisted field which would store that value for you and not have to re-compute it over and over again.
If you need to query a separate table (to e.g. check the existance of a row), you could still make this "HasProfile" a column in your entity table and just compute that field on a regular basis, e.g. every night or so. If you have the value stored as an atomic value, you don't need the computation every time. This works as long as that fact - has a profile or not - doesn't change too frequently.
To add a column to check whether or not EntityProfile is empty, do something like this:
CREATE FUNCTION CheckHasProfile(#Field VARCHAR(MAX))
RETURNS BIT
WITH SCHEMABINDING
AS BEGIN
DECLARE #Result BIT
IF #Field IS NULL OR LEN(#Field) <= 0
SET #Result = 0
ELSE
SET #Result = 1
RETURN #Result
END
and then add a new computed column to your table Entity:
ALTER TABLE dbo.Entity
ADD HasProfile AS dbo.CheckHasProfile(EntityProfile) PERSISTED
Now you have a BIT column and it's persisted, e.g. doesn't get computed every time to access the row, and should perform just fine!
At least in Postgres you can use the following statement:
SELECT EntityID, EntityName, EntityProfile IS NOT NULL AS HasProfile FROM Entity
What does the UDF EntityHasProfile() do?
Typically you could do something like this with a LEFT JOIN:
SELECT EntityId, EntityName, CASE WHEN EntityProfileIs IS NULL THEN 0 ELSE 1 END AS Has Profile
FROM Entities
LEFT JOIN EntityProfiles
ON EntityProfiles.EntityId = Entities.EntityId
This should eliminate a need for a costly scalar UDF call - in my experience, scalar UDFs should be a last resort for most database design problems in SQL Server - they are simply not good performers.

Resources