CAST statement with expression in SQL Server - sql-server

I am trying to write a SQL select which has this condition:
If distance supplied in variable is not null then select data less than the mentioned distance
If distance is null then select rows.
I want to use a CASE statement only
Below is my query
declare #distance int
set #distance = 3000
select
ROUND((dbo.GetDistance(#UserLat, #UserLon, Latitude, Longitude) * 1000), 0) AS Distance
from
messages
order by
Distance desc

You can use the following query:
SELECT *
FROM (
SELECT *,
ROUND((dbo.GetDistance(#UserLat, #UserLon, Latitude, Longitude)*1000),0) AS Distance
FROM messages ) s
WHERE (#distance IS NULL) OR (s.Distance < #distance)
ORDER BY Distance DESC
If #distance is null then all rows of messages will be returned, else only rows having s.Distance < #distance are selected.

Related

If subquery returns no rows, return 0 else return value of the subquery

I got a question and you can most likely help me.
I have got a query with a subquery that narrows the search. The query ends with this.
SELECT
*
FROM
Purchase
WHERE
Total < 5000
AND
Total >
(
SELECT TOP 1
buyamount
FROM
employee
WHERE
manager = 'TST'
ORDER BY
buyamount ASC
)
ORDER BY Date DESC
I want the value of the subquery after the ">"... but if the subquery gives nothing in return, i want it to be 0. How do i do this?
In the example you posted you could simply wrap the subquery in COALESCE and get your desired result.
SELECT
*
FROM
#Purchase
WHERE
Total < 5000
AND
Total > COALESCE(
(
SELECT TOP 1
buyamount
FROM
#employee
WHERE
manager = 'TST'
ORDER BY
buyamount ASC
), 0)
ORDER BY Date DESC
You could also use a variable in your provided example instead of a subquery. I prefer this approach because it separates the logic, making it easier to read. Subqueries can also be a performance killer. Having it broken out like this, it should perform better.
DECLARE #BuyAmount INT
SET #BuyAmount = (SELECT TOP 1 BuyAmount FROM #Employee WHERE Manager = 'TST' ORDER BY BuyAmount ASC)
IF #BuyAmount IS NULL SET #BuyAmount = 0
SELECT *
FROM #Purchase
WHERE Total < 5000 AND Total > #BuyAmount
ORDER BY Date DESC

Repeat last value of a column, when it is empty -SSRS

Can we repeat last value of a column in SSRS? As in attachment, all blank rows in
the last column should be filled with the latest value 702
I used Previous, Last functions but nothing helped
That's achievable if you do this:
Step 1. For your source, you build a sql query where you group the data by Year, AbsoluteMonth, etc.
So for each Year / AbsoluteMonth pair the report has only ONE value.
Step 2. Use below formula:
=IIf(IsNothing(Sum(Fields!Amt.Value)), Last(Fields!Amt.Value, "Year"), Sum(Fields!Amt.Value))
Here "Year" is group name, and Amt - your field name, which is probably R_Pax
Step3. (optional) Sort the data if it's not naturally sorted to provide the correct last value.
Step 1 is very important. Otherwise the cell with empty value will not show the last total, it will show the last value for a month, so if month (1) has values 30, 50, 60, and month (2) doesn't have any values, then it will show 60 for month(2), month(3), etc..., not sum(30+50+60).
You better insert the remaining blank records with last value into your dataset before pass the data to report.I assume your table is matrix.
DECLARE #Today DATETIME
SET #Today = GETDATE()
DECLARE #MatrixData TABLE (
Month1 INT
, Year1 INT
, Value INT
)
INSERT INTO #MatrixData (Month1, Year1, Value)
SELECT MONTH(DATEADD(MONTH, Id * -1, #Today)) AS Date1Month, YEAR(DATEADD(MONTH, Id * -1, #Today)) AS Date1Year, Id * 10 AS Value1
FROM (
SELECT TOP 60 ROW_NUMBER() OVER (ORDER BY Id) AS Id
FROM SysObjects
) A
ORDER BY Date1Year, Date1Month
SELECT * FROM #MatrixData
-- Insert blank month of last year with last value
INSERT INTO #MatrixData (Month1, Year1, Value)
SELECT A.RunningMonth, A1.MaxYear, A1.LastValue
FROM (
SELECT TOP 12 ROW_NUMBER() OVER (ORDER BY Id) AS RunningMonth
FROM SysObjects
) A
INNER JOIN (
-- Get Last Value in #MatrixData
SELECT A.MinMonth, A.MaxMonth, A.MaxYear, A1.Value AS LastValue
FROM (
-- Get Max Month Last Year in #MatrixData
SELECT MAX(A1.Month1) AS MinMonth, A.MaxMonth, A.MaxYear
FROM (
-- Get Max Month & Max Year
SELECT MAX(Month1) AS MaxMonth, MAX(Year1) AS MaxYear
FROM #MatrixData
) A
INNER JOIN #MatrixData A1 ON A.MaxYear = A1.Year1
GROUP BY A.MaxMonth, A.MaxYear
) A
INNER JOIN #MatrixData A1 ON A.MinMonth = A1.Month1 AND A.MaxYear = A1.Year1
) A1 ON A.RunningMonth > A1.MinMonth AND A.RunningMonth <= A1.MaxMonth
SELECT * FROM #MatrixData
We can do it at SQL end and fetch data to SSRS
Steps:
Do pivot if needed
Get the data at granularity column. Here it is Absolute Month
Then use the SQL method to replcae the Nulls/ last values which are empty with the last highest value
Ref:
`select a.AbsoluteMonth,Mon
,first_value(a.S1_pax)over(partition by a.v1_p order by num ) as S_Pax
,first_value(a.S2_pax)over(partition by a.v2_p order by num ) as S2_Pax`
from
(select *
,sum(case when S1_pax is null then 0 else 1 end) over (order by num) as v1_p
,sum(case when S2_pax is null then 0 else 1 end) over (order by num) as v2_p
from X_Table
)a
And fill all places respectively. Plz refer below output
In Oracle it is done like this. SQL Server has both COALESCE and LAG functions. So this must be possible with SQL Server also. There is also another stackoverflow question similar to this. Just could not locate it.
create table mytab(n number, m number);
insert into mytab values(1,null);
insert into mytab values(2,null);
insert into mytab values(3,44949);
insert into mytab values(4,null);
insert into mytab values(5,null);
insert into mytab values(6,null);
insert into mytab values(7,null);
insert into mytab values(8,null);
insert into mytab values(9,null);
insert into mytab values(10,null);
insert into mytab values(11,74631);
insert into mytab values(12,null);
insert into mytab values(13,null);
select t.*, coalesce(m, lag(m ignore nulls) over (order by n))
from mytab t;

Consuming values from 2 columns

I am trying to figure out how to make a sort of "consumption" query where an INT value column (X) is subtracted from another INT column (Y) until it reaches 0, then stop. The column DesiredResult and DesiredResultExplanation are here only for reference to the math being performed. This takes place in DESC date order (future consuming back to the present)
My initial approach was to use window functionality, but the problem is once the value (Y) reaches 0, it needs to stop performing a running total. Had similar issues using a CTE as well.
If changing the table structure will help at all, this can be done.
Version: SQL Server 2014 or higher
Thanks!
DECLARE #test TABLE
(
ID INT IDENTITY (1,1)
,PeriodDate DATE
,X INT
,Y INT
,DesiredResult INT
,DesiredResultExplanation VARCHAR(100)
)
INSERT INTO #test VALUES ('2017-05-01', 100,0, 100,'Nothing left to subtract. Value is unchanged')
INSERT INTO #test VALUES ('2017-05-08', 200,0, 200,'Nothing left to subtract. Value is unchanged')
INSERT INTO #test VALUES ('2017-05-15', 300,0, 100,'300 - 200 = 100 (Orig -1100 has been consumed)')
INSERT INTO #test VALUES ('2017-05-22', 400,0,-200,'400 - 600 = -200 ')
INSERT INTO #test VALUES ('2017-05-29', 500,-1100,-600, '500 - 1100 = -600')
SELECT *
FROM #test
ORDER BY PeriodDate DESC
DEMO
WITH cte as (
SELECT *,
SUM(X) OVER (ORDER BY PeriodDate DESC) accumulated
FROM #test
), parameter as (
SELECT 1100 as startY
)
SELECT *,
CASE WHEN accumulated <= startY
THEN accumulated - startY
WHEN LAG(accumulated) OVER (ORDER BY PeriodDate DESC) < startY
THEN accumulated - startY
ELSE X
END as newDesire
FROM cte
CROSS JOIN parameter
ORDER BY PeriodDate DESC;
OUTPUT
EDIT: You can change the LAG condition with
WHEN accumulated - X < startY

Calculate percentage in integer of a column in SQL SERVER?

I have just started experimenting how to calculate the percentage of a row. This is the code I write.
SELECT DISTINCT
ServiceName
COUNT(serviceID) AS Services
FROM Tester_DW
WHERE DateToday=20150410
GROUP BY ServiceName
How can calculate the percentage of the column Services above, and have the percentage in integer? Is it easier to calculate the percentage of the code example if I put my query result in a #temp table and calculate the percentage from the #temp or is it possible to calculate the percentage in integer% on the fly?
ADDED:Output sketch
ServiceName|Services| % of Total
--------------------------------
TV-cable | 4500 | 40%
--------------------------------
Mobile BB | 3000 | 10%
--------------------------------
MOBILE wifi| 20 | 5%
--------------------------------
It is hard to get it right, because you should deal with the sum of rounded integer percentage to get it 100% in total.
Using Largest Remainder Method
;WITH x AS
(
SELECT
ServiceName,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER () AS [Percent],
FLOOR(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER ()) AS [IntPercent],
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER () % 1 AS [Remainder]
FROM Tester_DW
GROUP BY ServiceName
)
SELECT ServiceName, IntPercent + CASE WHEN Priority <= Gap THEN 1 ELSE 0 END AS IntPercent
FROM
(
SELECT *, ROW_NUMBER() OVER (ORDER BY Remainder DESC) AS Priority, 100 - SUM(IntPercent) OVER () AS Gap FROM x
) data
Percentage is a count divided by the overall (reference) count. You have to use an inner query to get that overall count:
SELECT ServiceName, COUNT(serviceID) AS Services,
FLOOR(COUNT(serviceID) / (SELECT COUNT(serviceID) FROM Tester_DW)) AS percent
FROM Tester_DW WHERE...
Depending on the output you want (that is, what your reference count is), you may have to add the WHERE clause (or parts of it) in the inner query as well.
Select
ServiceName,
Count(ServiceName) as Services,
(Count(ServiceName)* 100 / (Select Count(*) From Tester_DW)) as [% of total]
From Tester_DW
Group By ServiceName
You simply divide the count of a single service by the amount of all services.
Try with window functions:
Create table t(id int)
Insert into t values
(1),(1),(2)
Select * from (Select id,
100.0*count(*) over(partition by id)/ count(*) over() p
from t) t
Group by id, p

How to query number based SQL Sets with Ranges in SQL

What I'm looking for is a way in MSSQL to create a complex IN or LIKE clause that contains a SET of values, some of which will be ranges.
Sort of like this, there are some single numbers, but also some ranges of numbers.
EX: SELECT * FROM table WHERE field LIKE/IN '1-10, 13, 24, 51-60'
I need to find a way to do this WITHOUT having to specify every number in the ranges separately AND without having to say "field LIKE blah OR field BETWEEN blah AND blah OR field LIKE blah.
This is just a simple example but the real query will have many groups and large ranges in it so all the OR's will not work.
One fairly easy way to do this would be to load a temp table with your values/ranges:
CREATE TABLE #Ranges (ValA int, ValB int)
INSERT INTO #Ranges
VALUES
(1, 10)
,(13, NULL)
,(24, NULL)
,(51,60)
SELECT *
FROM Table t
JOIN #Ranges R
ON (t.Field = R.ValA AND R.ValB IS NULL)
OR (t.Field BETWEEN R.ValA and R.ValB AND R.ValB IS NOT NULL)
The BETWEEN won't scale that well, though, so you may want to consider expanding this to include all values and eliminating ranges.
You can do this with CTEs.
First, create a numbers/tally table if you don't already have one (it might be better to make it permanent instead of temporary if you are going to use it a lot):
;WITH Numbers AS
(
SELECT
1 as Value
UNION ALL
SELECT
Numbers.Value + 1
FROM
Numbers
)
SELECT TOP 1000
Value
INTO ##Numbers
FROM
Numbers
OPTION (MAXRECURSION 1000)
Then you can use a CTE to parse the comma delimited string and join the ranges with the numbers table to get the "NewValue" column which contains the whole list of numbers you are looking for:
DECLARE #TestData varchar(50) = '1-10,13,24,51-60'
;WITH CTE AS
(
SELECT
1 AS RowCounter,
1 AS StartPosition,
CHARINDEX(',',#TestData) AS EndPosition
UNION ALL
SELECT
CTE.RowCounter + 1,
EndPosition + 1,
CHARINDEX(',',#TestData, CTE.EndPosition+1)
FROM CTE
WHERE
CTE.EndPosition > 0
)
SELECT
u.Value,
u.StartValue,
u.EndValue,
n.Value as NewValue
FROM
(
SELECT
Value,
SUBSTRING(Value,1,CASE WHEN CHARINDEX('-',Value) > 0 THEN CHARINDEX('-',Value)-1 ELSE LEN(Value) END) AS StartValue,
SUBSTRING(Value,CASE WHEN CHARINDEX('-',Value) > 0 THEN CHARINDEX('-',Value)+1 ELSE 1 END,LEN(Value)- CHARINDEX('-',Value)) AS EndValue
FROM
(
SELECT
SUBSTRING(#TestData, StartPosition, CASE WHEN EndPosition > 0 THEN EndPosition-StartPosition ELSE LEN(#TestData)-StartPosition+1 END) AS Value
FROM
CTE
)t
)u INNER JOIN ##Numbers n ON n.Value BETWEEN u.StartValue AND u.EndValue
All you would need to do once you have that is query the results using an IN statement, so something like
SELECT * FROM MyTable WHERE Value IN (SELECT NewValue FROM (/*subquery from above*/)t)

Resources