How to get this column result in SQL Server - sql

Looking to try to return the dataset below in SQL Server. I have the 2 columns TrailerPosition & Divider and looking to also return Zone. Zone would be calculated as starting with Zone 1 and then would change to zone 2 on the record after divider = 1. And then to 3 after the next record where Divider = 1. The screenshot below looks like the column I'm trying to return.
any ideas how this can be done in SQL Server?
Test data for the below:
declare #t table (TrailerPosition nvarchar(5),Divider bit);
insert into #t values ('01L',0),('01R',0),('02L',0),('02R',1),('03L',1),('03R',0),('04L',0),('04R',0),('05L',1),('05R',1),('06L',0),('06R',0),('07L',0),('07R',0),('08L',0),('08R',0),('09L',0),('09R',0),('10L',0),('10R',0),('11L',0),('11R',0),('12L',0),('12R',0),('13L',0),('13R',0),('14L',0),('14R',0),('15L',0),('15R',0);

If 2012+, the window functions would be a nice fit here
Select TrailerPosition
,Divider
,Zone = 1+sum(Flag) over (Order By TrailerPosition)
From (
Select *
,Flag = case when Lag(Divider,1) over (Order By TrailerPosition) =1 and Divider=0 then 1 else 0 end
From YourTable
) A
Returns

So, the Zone = 1 + The number of previous rows with the divider value of 0 and the previous row having a divider value of 1.
UPDATED
SELECT TrailerPosition, Divider,
(SELECT COUNT(*)
FROM #MyTable T1
WHERE (T1.TrailerPosition <= t0.TrailerPosition)
AND (T1.Divider = 0)
AND (SELECT Divider
FROM #MyTable t2
WHERE T2.TrailerPosition =
(SELECT MAX(T3.TrailerPosition)
FROM #MyTable T3
WHERE T3.TrailerPosition < T1.TrailerPosition)) = 1) + 1
AS Zone
FROM #MyTable t0

Related

how to scan each row of a table, and update current row based on previous row?

I need to update the current row using the following logic:
if current row is null, then set it as previous row
if current row is not null, then no action
the 1st row is not null, then NULL appears randomly
Those NULLs need to be updated using the logic previously mentioned
e.g.
1. 1
2. null
3. null
4. 2
5. null
6. null
needs to be updated as
1. 1
2. 1
3. 1
4. 2
5. 2
6. 2
How to do it in SQL?
Thanks
r
In case of two Null values in a row, you need to define the least non-null value of the table, so I think Outer Apply will handle your problem:
CREATE TABLE #TB(ID Int Identity(1, 1), Value Int)
INSERT INTO #TB([Value]) VALUES(1),(Null),(Null),(2),(Null),(Null)
UPDATE G SET G.Value = GG.Value
FROM
#TB AS G
OUTER APPLY
(SELECT
TOP 1 *
FROM
#TB AS GG
WHERE
GG.Value IS NOT NULL
AND
GG.ID < G.ID
ORDER BY
GG.ID DESC
) AS GG
WHERE
G.Value IS NULL
SELECT * FROM #TB AS T
but note, that if the first value is Null it will not give you the results, as you have not defined the logic for this scenario.
This might help:
SELECT
t1.col1,
t1.col2 AS previous,
(SELECT
t2.col2
FROM table_1 t2
WHERE t2.col1 = (SELECT
MAX(t3.col1)
FROM table_1 t3
WHERE t3.col1 <= t1.col1
AND col2 IS NOT NULL))
AS new
FROM table_1 t1;
result
Where are you using this SQL code? If you are using Hive SQL for example, there is a function which allows you to directly get last non null value:
LAST_VALUE(col, true) over (PARTITION BY id ORDER BY date)
Oracle 10g has also a function to do this, as adressed in this thread:
Fill null values with last non-null amount - Oracle SQL
Are you familiar with window functions?
while (select count(*) FROM Table_1 where c1_derived = '') > 0
begin
update top(1) Table_1
set c1_derived = (select c1_derived from Table_1 t2 where (t2.id = [Table_1].id-1))
where c1_derived = ''
end
Try the below script. (sql 2008 +)
CREATE TABLE #table(id Int Identity(1, 1), value Int)
INSERT INTO #table([Value]) VALUES(1),(Null),(Null),(2),(Null),(Null)
;WITH cte AS
(
SELECT ID,Value,ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS row
FROM #table
)
SELECT a.ID,max(b.Value)
FROM cte a
INNER JOIN cte b ON a.row >=b.row
GROUP BY a.ID
drop table #table
Edit2 this also another script using "UNBOUNDED PRECEDING "
CREATE TABLE #table(id Int Identity(1, 1), value Int)
INSERT INTO #table([Value]) VALUES(1),(Null),(Null),(2),(Null),(Null)
select * ,max(t.value) over(order by Id Rows UNBOUNDED PRECEDING) maxValue
from #table t
drop table #table
check this link about "OVER Clause"
https://learn.microsoft.com/en-us/sql/t-sql/queries/select-over-clause-transact-sql

Get the specified decimal part having data only from the sql query

select * from payments where amount = 0 order by id desc
I need to get the values which it has 0.5 part only.Could you tell me how to do that ?
e.g. 12,12.5,12.6,14.5
results data set can be have : 12.5,14.5 values data only
Truncate amount to integer, subtract from original value and compare with 0.5
SELECT *
FROM payments
WHERE amount - CAST(amount AS INT) = 0.5
ORDER BY id DESC
OR:
SELECT *
FROM payments
WHERE amount - FLOOR(amount) = 0.5
ORDER BY id DESC
Hacks way:
SELECT *
FROM payments
WHERE PARSENAME(amount, 1)= 5
ORDER BY id DESC
One more using modulo:
SELECT *
FROM payments
WHERE amount % 1 = 0.5
ORDER BY id DESC
you can find this way also
declare #temp table (val decimal(18,3))
insert into #temp values (0.5),(10.5),(20.8)
select * from #temp
where (val % 1) = 0.5
Same idea as accepted answer, just another thought:
SELECT *
FROM payments
WHERE FLOOR(amount) < CEILING(amount) -- or <>
ORDER BY id DESC
or
WHERE FLOOR(amount) + 1 = CEILING(amount)

Stored procedure order of execution

CREATE PROCEDURE [dbo].[uspGetLogs]
(
#StartDate DATETIME,
#EndDate DATETIME
)
AS
SELECT
sl.ID,
LOG10(sl.Value)
FROM
dbo.SampleList sl
INNER JOIN
(
SELECT
ID,
RANK() OVER(PARTITION BY Codec ORDER BY TimeStampUTC DESC, d.ID DESC) ranked
FROM
dbo.SampleList
WHERE
ListDate BETWEEN #StartDate AND #EndDate
) r
ON
r.ID = sl.ID AND
r.ranked = 1
I tried this stored procedure with this #StartDate = 2014-01-29 #EndDate = 2015-03-14.
And gets this error
An invalid floating point operation occurred
The reason of the error "An Invalid floating point operation occured" is the invalid usage of mathematical function.
SELECT LOG10(-3);
SELECT LOG10(0);
If the above functions are run it will return the error.
I able to get a single value from the whole table set where value is less than one. But the ListDate for that value is 2015-03-14 so it should not be included because it is not coverted by the date range passed in the stored procedure.
So it seems that the stored procedure executes the function in the whole set first before joining and filtering the dataset with date range.
Is this expected?
I think there could be a data issue here as the basic logic of your code doesn't cause an issue, see the below sample:
CREATE TABLE #temp1 ( id INT, val INT )
CREATE TABLE #temp2 ( id INT, val INT )
INSERT INTO #temp1 ( id, val )
VALUES ( 1, 1 ), ( 2, 10 ), ( 3, -1 ) -- Negative value for id=3 exculded in subquery
INSERT INTO #temp2 ( id, val )
VALUES ( 1, 1 ), ( 2, 10 ), ( 3, 20 )
SELECT t1.id ,
LOG10(t1.val) AS Val
FROM #temp1 t1
INNER JOIN ( SELECT * ,
RANK() OVER ( PARTITION BY id ORDER BY val ) ranked
FROM #temp2
WHERE id BETWEEN 1 AND 2 -- excludes id 3
) t2 ON t2.id = t1.id
AND t2.ranked = 1
DROP TABLE #temp1
DROP TABLE #temp2
Produces:
id val
1 0
2 1
If you modify the BETWEEN clause to WHERE id BETWEEN 1 AND 3, you do see the error as the negative value is included.
So I'd triple check the data and if there's still an issue, try to post a small sample that recreates the issue.
I dont think there's a guaranteed point where function gets executed, so my answer is it depends, it depends on query plan, on how "early" the optimizer decides to execute the function - before or after the join. To make sure the function is only executed for valid values, you can change to:
CASE WHEN sl.Value < 1 THEN 0 ELSE LOG10(sl.Value) END

SQL - How to list all tuples in a relation such that tuple 1 is greater than tuple 2

Suppose that I have a relation with only 1 column "Value (INT)" and its values are in descending order.
+----------+
+ VALUE +
+----------+
+ 10 +
+ 9 +
+ 8 +
+ 7 +
....
How can list all the combinations which contains two tuples such that the first tuple is greater than the second tuple
Note: It may exist two tuples with same value
The desired outputs should be like: (10,9) (10, 8) (9,8), (9,7) (8,7)
You can do a cross join on the same table.
SELECT t1.VALUE AS VALUE1, t2.VALUE AS VALUE2
FROM thing t1 JOIN thing t2 ON (t1.VALUE != t2.VALUE AND t1.VALUE > t2.VALUE)
I understand that a single tuple may apear only twice on the left side of the resultset, am I right? That's why there is no (10,7)?
Then you need to compute row number.
select t1.value, t2.value
from
(
select t.value, row_number(order by t.value) as rnum
from table t
) t1 inner join
(
select t.value, row_number(order by t.value) as rnum
from table t
) t2 on t1.value > t2.value and t1.rnum < t2.rnum + 2
Performance of this query will be pretty bad, but I don't know what database are you using - I've used MS SQL row_number function.
Another idea:
If you are using SQL Server 2012+ and your answer to the question posed at the begining of this post is positive, you can use:
select t.value, lead(t.value,1,0) over(order by t.value desc) as lead1
lead(t.value,2,0) over(order by t.value desc) as lead2
from table t
You may need to handle 0 (defulat value if there is no "lead" tuple). I'm not sure if output in this form is acceptable.
And here you go with cursor solution:
DECLARE #result TABLE
(
value1 int,
value2 int
);
DECLARE
#value int,
#lag1 int,
#lag2 int
DECLARE c CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
SELECT value
FROM table
ORDER BY value desc
OPEN c;
FETCH NEXT FROM c INTO #lag2;
FETCH NEXT FROM c INTO #lag1;
FETCH NEXT FROM c INTO #value;
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT #result(value1, value2) SELECT #lag2, #lag1
INSERT #result(value1, value2) SELECT #lag2, #value
SET #lag2 = #lag1
SET #lag1 = #value
FETCH NEXT FROM c INTO #value
END
CLOSE c;
Again, I used MS SQL syntax. If you write how you want duplicates handled, I can update the solution.

SQL-how to bring the value of first row to the second row

I have a table with following values:
week_no amt amt_diff
1 500 100
2 600 300
3 900 100
4 1000 null
When I subtract week2.amt-week1.amt the difference is getting saved in the amt_diff column of week_no=1. But I want the result to be stored with the week_no=2 record.
Can anyone help me with the SQL?
I think this should work. You can make it a SELECT first to make sure you get the desired results. The syntax is valid in SQL Server, not sure about other RDBMS.
UPDATE m2
SET amt_diff = (m2.amt-m1.amt)
FROM MyTable m2
INNER JOIN MyTable m1
ON m1.week_no = (M2.week_no - 1)
It will update all records that have week after it to be calculated.
To just select the values:
SELECT amt_diff = (m2.amt-m1.amt)
FROM MyTable m2
INNER JOIN MyTable m1
ON m1.week_no = (M2.week_no - 1)
UPDATE YOURTABLE T
SET T.AMT_DIFF = ( T.AMT - NVL(( SELECT TT.AMT
FROM YOURTABLE TT
WHERE TT.WEEK_NO = (T.WEEK_NO - 1)
)
,0)
)
WHERE T.WEEK_NO = 2;
Might work for you.
update k
set k.amt_diff=(select k2.amt from week k2 where week_no=k.week_no+1)-amt
from week k