Update rows based on range around values without changing rows that are not initially within range - sql

In a local SQLite (vs 3.29.0) database, there is a table with 3 columns (excluding the rowID). Two contain values, and one contains categories. I want to update the category based on a range around the values of one specific category. It needs to be possible that the category that is SET is the same category as the one that determines the range.
Example:
id
Value
Value2
Category
1
20
20
2
2
30
30
2
3
40
40
2
4
70
70
2
5
5
5
1
6
19
19
1
7
26
26
1
8
42
42
1
9
49
49
1
10
52
52
1
11
71
71
1
12
90
90
1
13
17
17
1
I want rows to be changed to category 2, based on a range of 4 around value and a range of 2 around value2. This should change only rows 6, 9 and 11:
id
Value
Value2
Category
1
20
20
2
2
30
30
2
3
40
40
2
4
70
70
2
5
5
5
1
6
19
19
2
7
26
26
1
8
42
42
2
9
49
49
1
10
52
52
1
11
71
71
2
12
90
90
1
13
17
17
1
My current SQL statement is:
UPDATE tablename
SET Category = 2
WHERE (Category != 2
AND EXISTS (
SELECT *
FROM tablename t
WHERE t.Category = 2
AND tablename.Value BETWEEN t.Value - 4 AND t.Value + 4
AND tablename.Value2 BETWEEN t.Value2 -2 AND t.Value2 +2)
);
of which the result is:
id
Value
Value2
Category
1
20
20
2
2
30
30
2
3
40
40
2
4
70
70
2
5
5
5
1
6
19
19
2
7
26
26
1
8
42
42
2
9
49
49
1
10
52
52
1
11
71
71
2
12
90
90
1
13
17
17
2
What appears to be happening is that due to row 6 changing to category 2, row 13 is now within range of the values of a row that is in category 2, and therefore is also assigned category 2. How do I change the statement so that the SET is only applied to the values that were within range initially?
See the demo for the example.

If your version of SQLite is 3.33.0+ you can use the join-like UPDATE...FROM syntax to perform a self join in the UPDATE statement:
UPDATE tablename AS t1
SET Category = t2.Category
FROM tablename AS t2
WHERE t2.Category = 2
AND t1.Category <> t2.Category
AND t1.Value BETWEEN t2.Value - 4 AND t2.Value + 4
AND t1.Value2 BETWEEN t2.Value2 - 2 AND t2.Value2 + 2;
For previous versions of SQLite, first create a temporary table with all the rows of the table with Category = 2:
CREATE TEMPORARY TABLE t AS
SELECT * FROM tablename WHERE Category = 2;
and then update the table:
UPDATE tablename
SET Category = 2
WHERE Category <> 2
AND EXISTS (
SELECT 1
FROM t
WHERE tablename.Value BETWEEN t.Value - 4 AND t.Value + 4
AND tablename.Value2 BETWEEN t.Value2 -2 AND t.Value2 + 2
);
See the demo.

Related

Bigquery delete rows that are present in another table

I want to delete all the rows from table t1 which are present in table t2.
table_1 is as follows
a b c
1 4 3
3 334 3
5 4 5
6 5 4
4 85 3
7 332 54
8 46 6
45 42 5
7 576 6
and table 2 is as follows
a b c
7 332 54
3 334 3
7 576 6
as mentioned I would like to delete all the rows from table t1 which are present in table t2.
So I used the code
DELETE `projectname.datasetname.table1` t
WHERE t IN (SELECT * from `projectname.datasetname.table2`)
but it doesn't work, what would be the ideal solution here?
My desired result is
a b c
1 4 3
5 4 5
6 5 4
4 85 3
8 46 6
45 42 5
Thanks
If you need to look at the entire record, you can use:
DELETE `projectname.datasetname.table1` t
WHERE EXISTS (SELECT 1
FROM `projectname.datasetname.table2` t2
WHERE TO_JSON_STRING(t2) = TO_JSON_STRING(t)
);
However, normally a comparison of a simple id column of some sort is usually sufficient for such comparisons.
Use below
DELETE `projectname.datasetname.table1` t
WHERE TO_JSON_STRING(t) IN (SELECT TO_JSON_STRING(t) from `projectname.datasetname.table2` t);

In SQL, how to select minimum value of a column and group by other columns?

I have a lookup table below:
id ref order
1 6 0
2 6 0
3 7 0
5 34 0
6 33 0
6 255 1
9 12 0
9 80 1
12 7 0
12 76 1
13 10 0
15 12 0
16 6 0
16 7 1
17 6 1
17 63 0
18 7 0
19 7 1
19 75 0
20 6 0
20 63 1
So in the lookup table (tab_lkp), it has column [id] (the IDs of entities), [ref] (the reference id that points to other entities in another table) and [order] (tells the order of reference, smaller order means higher priority).
My expectation is that, for each of the IDs, only one ref with the smallest order is selected. My code is (by following Phil's answer):
select id
, ref
, min_order = min(order)
from [dbo].[tab_lkp]
group by id, ref
order by id, ref
But the code doesn't work for me, the results still contains multiple records for each of the IDs:
id ref order
1 6 0
2 6 0
3 7 0
5 34 0
6 33 0
6 255 1
9 12 0
9 80 1
12 7 0
12 76 1
13 10 0
15 12 0
16 6 0
16 7 1
17 6 1
17 63 0
18 7 0
19 7 1
19 75 0
20 6 0
20 63 1
Could you please let me know what is wrong with my code? And how should I achieve my goal?
From an ANSI sql approach:
select x2.id, x2.ref, x2.order
from MyTable x2
inner join
(
select id, min(order) as min_order
from MyTable
group by id
) x1
on x1.id = x2.id
and x1.min_order = x2.order
You would normally do this using row_number():
select t.*
from (select t.*, row_number() over (partition by id order by ref) as seqnum
from [dbo].[tab_lkp] t
) t
where seqnum = 1;
or by using a subquery that does exactly what you state that you want,
"for each of the IDs, only one ref with the smallest order is selected"
Select * from tab_lkp t
Where order =
(Select Min(order) from tab_lkp
where Id = t.Id)

Select for running total rank based on column values

I have problem while assigning the Ranks for the below scenarios.In my scenario running total calculated based on the Cnt field.
My sql query should return Rank values like below output. Per page it should accept only 40 rows, so im assigning ranks contain only 40 records. If the running total crossing 40 it should change ranks. For each count 40 it should change the rank values.
It would great help if I can get sql query to return values
select f1,f2,sum(f2) over(order by f1) runnign_total
from [dbo].[Sheet1$]
OutPut:
ID cnt Running Total Rank
1 4 4 1
2 5 9 1
3 4 13 1
4 4 17 1
5 4 21 1
6 5 26 1
7 4 30 1
8 4 34 1
9 4 38 1
10 4 42 2
11 4 46 2
12 4 50 2
13 4 54 2
14 4 58 2
15 4 62 2
16 4 66 2
17 4 70 2
18 4 74 2
19 4 78 2
20 4 82 3
21 4 86 3
22 4 90 3
select f1,f2,sum(f2) over(order by f1) running_total, Floor(sum(f2) over(order by f1) / 40) [rank]
from [dbo].[Sheet1$]

Case statements on where clause?

I am a bit new to case statements but below is my query.
Table1
col1 col2 col3 col4 Month Freeze
1 13 25 37 1 0
3 15 27 39 2 1
4 16 28 40 2 0
5 17 29 41 3 1
6 18 30 42 3 0
7 19 31 43 4 1
8 20 32 44 4 0
9 21 33 45 5 1
10 22 34 46 5 0
11 23 35 47 6 0
'Results i want like':
Select all records from Month1 to Month 12 where 'Freeze = 1' ,
if any month between 1 to 12 does not have Freeze = 1 then give me records for Freeze= 0 'just for the months which does not have 'Freeze = 1'
My best non-working attempt:
select * from tb1 where Month between 2 and 6 and freeze = 1 or Month in ('1' ,'2') and freeze = 0
Here is one option using an outer join:
select t1.*
from Table1 t1
left join Table1 t2 on
t1.month = t2.month and t2.freeze = 1
where t1.freeze = 1
and t1.month between 1 and 12
or t2.month is null
SQL Fiddle Demo
Results:
COL1 COL2 COL3 COL4 MONTH FREEZE
1 13 25 37 1 0
3 15 27 39 2 1
5 17 29 41 3 1
7 19 31 43 4 1
9 21 33 45 5 1
11 23 35 47 6 0
Note, depending on your data, you may need to use distinct with this method.
The following is a method that uses standard SQL:
Select t1.*
from table1 t1
where Freeze = 1 and month between 1 and 12 or
(not exists (select 1 from table where Freeze = 1 and month between 1 and 12) and
month = 0
);
Using window functions, you could also do:
select t1.*
from (select t1.*,
count(case when freeze = 1 and month between 1 and 2) over () as freeze1cnt
from table1 t1
) t1
where month between 1 and 2 and freeze = 1 or
month = 0 and freeze1cnt = 0;

Result from CTE query not sorted by Level, why?

Update:
I asked the question because the result does not look like how it is executed... which is explained here:
http://msdn.microsoft.com/en-us/library/ms186243(v=sql.105).aspx
Well I have another question...
If I do not need the same parents and the Level, is it possible to return one parent only once using the CTE (not the select after it) or maybe some other sql?
===========================
Desired result is like:
......
54 4
**** the above is anchor member, the numbers are correct
4 1
1 0
2 1
36 35
35 8
8 1
54 12
12 1
11 1
3 1
===========================
I am using a recursive query to find out all the parents from a Hierarchy table, for items from Items table; I thought the result should be by Level, but it is not... I know I can sort it using order by, I just think the output itself should be ordered by Level because the Recursive Memeber is run by Level, right?
WITH Result(ItemID, ParentID, Level)
AS
(
--get the anchor member from tbItems
SELECT itemID, itemParentID, 0 AS Level
FROM tbItems WHERE
approved = 0
UNION ALL
--recursive member from tbHierarchy
SELECT h.hierarchyItemID, h.parentItemID, Level + 1
FROM tbHierarchy AS h
INNER JOIN
Result AS r
ON
h.hierarchyItemID = r.ParentID
)
SELECT *
FROM Result
the Result is:
ItemID ParentID Level
----------- ----------- -----------
7 3 0
11 2 0
18 11 0
19 11 0
21 54 0
31 2 0
33 36 0
34 36 0
35 36 0
36 36 0
38 2 0
39 2 0
40 2 0
54 4 0
**** the above is anchor member, the numbers are correct
4 1 1
1 0 2
2 1 1
1 0 2
2 1 1
1 0 2
2 1 1
1 0 2
36 35 1
35 8 2
8 1 3
1 0 4
36 35 1
35 8 2
8 1 3
1 0 4
36 35 1
35 8 2
8 1 3
1 0 4
36 35 1
35 8 2
8 1 3
1 0 4
2 1 1
1 0 2
54 12 1
12 1 2
1 0 3
11 1 1
1 0 2
11 1 1
1 0 2
2 1 1
1 0 2
3 1 1
1 0 2
The data in a database are not ordered. If you do not put an order by, you cannot be sure of the order of the output.
Never think the database will do things the way you think, 90% of the time, it's wrong. The objective of database is to understand your query and find the fastest way to find the solution, and it's often not the way you think.
Depending of your editor, but sometimes you can ask it to explain the plan used to find the solution, it might give you some explanation of how the result is obtained.