Select values that are within a certain range but exclude the values that determine the range in Sqlite - sql

In a SQLite local database, I have a 2 column-table, one containing values, and one containing categories. I want to update the categories of certain rows based on the following selection:
select the rows that are in a certain category
determine the values for those rows.
select the rows that have values within a certain range of the values of the already selected rows.
update the rows that are within the second selection, but exclude those that are in the first selection.
The statement that I have now (that does not work) is as follows:
UPDATE table SET category = '3' WHERE
(
value BETWEEN
(
((SELECT value FROM table WHERE category = '2') +4)
AND
((SELECT value FROM table WHERE category = '2') -4)
EXCEPT SELECT value FROM table WHERE category = '2'
)
... (further constraints)
)
This runs without error, but does not actually appear to select anything, as nothing is updated. What is the correct way to get this update to work?
EDIT: as requested an example with tables:
rowid
Value
Category
1
20
2
2
30
2
3
40
2
4
70
2
5
5
1
6
19
1
7
26
1
8
42
1
9
49
1
10
52
1
11
71
1
12
90
1
I want the values of the rows that are currently in category 1, to be placed in category 3, based on a range of 4 around the values of the rows that are in category 2. So in this case any row that has category = 1, that has a value of either 16-24, 26-34, 36-44 or 66-74.
rowid
Value
Category
1
20
2
2
30
2
3
40
2
4
70
2
5
5
1
6
19
3
7
26
3
8
42
3
9
49
1
10
52
1
11
71
3
12
90
1

You can use EXISTS:
UPDATE tablename
SET Category = 3
WHERE Category = 1
AND EXISTS (
SELECT 1
FROM tablename t
WHERE t.Category = 2
AND tablename.Value BETWEEN t.Value - 4 AND t.Value + 4
);
See the demo.

Related

If value is 0 (zero) then increment it with max number +1

I need to UPDATE all new inserted values of 0 with the highest value from the same column + 1. Any value with zero should be updated by the highest value +1. If the highest value is 30 below in the "Preference" column, then the next value should be 31 for Id 11 and 32 for Id 12. New values are inserted every 30 seconds, could be multiple, from the source table that I have no access to into the table below (table 1).
The UPDATE statement is executed when a user drags and drops a row in the web app.
UPDATE [DB].[dbo].[tbl1] SET
Preference = #Preference
WHERE Id = Id
I need to somehow add that logic to this statement described above. This is where I am lost.
Any ideas? Thank you for the help!!
For example:
ID
Preference
Account
3
7
22
6
8
33
7
9
44
9
0
55
11
0
66
Required results:
ID
Preference
Account
3
7
22
6
8
33
7
9
44
9
10
55
11
11
66
Gather the current maximum preference using a cross apply (or you could use a cross join) and together with row_number() ordered by ID you will increment preference as described:
with CTE as (
select id, preference, cp.maxpref, row_number() over(order by id) rn
from mytable
cross apply (select max(preference) maxpref
from mytable p
) cp
where preference = 0
)
update cte
set preference = maxpref + rn
where preference = 0
see db<>fiddle here
select *
from mytable
order by id
ID
Preference
Account
3
7
22
6
8
33
7
9
44
9
10
55
11
11
66

Selecting row from group of row based on a column value [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Closed 4 years ago.
Have following table
PkId ProfileId Status Amount
1 234 0 10
2 235 1 100
3 236 0 50
4 236 1 80
5 237 0 70
For row number 3 and 4 Profile Id is same but I want in resultset it should consider row having value 0 so whereever we have rows where Profile Id is same it should pick row where status =0 And for rest of the rows it should pick as it is.
Expected Result Set:
PkId ProfileId Status Amount
1 234 0 10
2 235 1 100
3 236 0 50
5 237 0 70
In expected result set row number 4 has to be omitted because :
236 Id at 4 is already there row number 3
Value of Status is 1 in row number 4.
you could use a join on subselect for min status
select * from my_table m
inner join (
select ProfileId , min(Status ) min_status
from my_table
group by ProfileId
) t on t.profileId = m.profileId and t.min_status = m.status

Add order within group and mark whether a row is the last in its group

I have a table in an SQL Server database on the following form, sorted according to id.
id group
1 10
17 10
24 10
2 20
16 20
72 20
104 20
8 30
9 30
I would like to select every row grouped according to the row group and add the following information to this table: the order (as sorted) within the group and whether the row is the last row in the group. In other words, something similar to this:
id group order last
1 10 1 0
17 10 2 0
24 10 3 1
2 20 1 0
16 20 2 0
72 20 3 0
104 20 4 1
8 30 1 0
9 30 2 1
I've tried fiddling around with ROW_NUMBER, but I'm not all that experienced with SQL Server and I can't get it to work. Does anyone have a suggestion?
Use ROW_NUMBER window function
select id,[group],
row_number()over(partition by [group] order by id) as [order],
case when row_number()over(partition by [group] order by id desc) = 1 then 1 else 0 end as Last
From yourtable

How to compare between two values in one field depending on another field in SQL* / Oracle Reports?

I have three fields in a table named table_family.
One is "relation" it has 3 kinds of values 1 for person, 2 for parents, 3 for son.
Other field is "age" and it contains the ages of all the relations in the first table.
The 3rd field is for the family number.
Now the relation could be repeated like four sons and two parents for the same family
This is an example:
Family Relation Age
1 1 25
1 2 40
1 2 35
1 3 30
1 3 28
1 3 29
2 1 30
2 2 40
2 3 12
Now I want to build a report that displays an error which is:
Whenever the difference between parent (relation=2) and son (relation=3) is less than 12, then this is an error.
I tried this code :
select family from table_family
group by family
having
SELECT family
FROM table_FAMILY
GROUP BY
family
HAVING
((
MAX(DECODE ( relation,2,age ))
- MAX(DECODE ( relation,3,age ))
)
<12 )
OR
(
(SELECT MIN(age) FROM table_FAMILY WHERE relation=2) -(SELECT MIN(age) FROM table_FAMILY WHERE relation=3) < 12
)
If I use this code then only the maximum and minimum values will be compared. The values in between will be ignored but I need them to be compared in the same way.
The output report will be something like:
family difference between parent and son
1 10
1 12
1 11
1 5
1 7
1 6
This what I need in the final report
For family = 1 I subtracted parent age - son age =
40-30=10
40-28=12
40-29=11
35-30=5
35-28=7
35-29=6
But for the 2nd family
40-12= 28 >12 then should not be listed (not an error).
Any help please?
Something like this maybe?
select *
from table_family f1 join table_family f2 on f1.family = f2.family
where f1.relation = 2 and f2.relation = 3
and f1.age - f2.age <= 12

How to track how many times a column changed its value?

I have a table called crewWork as follows :
CREATE TABLE crewWork(
FloorNumber int, AptNumber int, WorkType int, simTime int )
After the table was populated, I need to know how many times a change in apt occurred and how many times a change in floor occurred. Usually I expect to find 10 rows on each apt and 40-50 on each floor.
I could just write a scalar function for that, but I was wondering if there's any way to do that in t-SQL without having to write scalar functions.
Thanks
The data will look like this:
FloorNumber AptNumber WorkType simTime
1 1 12 10
1 1 12 25
1 1 13 35
1 1 13 47
1 2 12 52
1 2 12 59
1 2 13 68
1 1 14 75
1 4 12 79
1 4 12 89
1 4 13 92
1 4 14 105
1 3 12 115
1 3 13 129
1 3 14 138
2 1 12 142
2 1 12 150
2 1 14 168
2 1 14 171
2 3 12 180
2 3 13 190
2 3 13 200
2 3 14 205
3 3 14 216
3 4 12 228
3 4 12 231
3 4 14 249
3 4 13 260
3 1 12 280
3 1 13 295
2 1 14 315
2 2 12 328
2 2 14 346
I need the information for a report, I don't need to store it anywhere.
If you use the accepted answer as written now (1/6/2023), you get correct results with the OP dataset, but I think you can get wrong results with other data.
CONFIRMED: ACCEPTED ANSWER HAS A MISTAKE (as of 1/6/2023)
I explain the potential for wrong results in my comments on the accepted answer.
In this db<>fiddle, I demonstrate the wrong results. I use a slightly modified form of accepted answer (my syntax works in SQL Server and PostgreSQL). I use a slightly modified form of the OP's data (I change two rows). I demonstrate how the accepted answer can be changed slightly, to produce correct results.
The accepted answer is clever but needs a small change to produce correct results (as demonstrated in the above db<>fiddle and described here:
Instead of doing this as seen in the accepted answer COUNT(DISTINCT AptGroup)...
You should do thisCOUNT(DISTINCT CONCAT(AptGroup, '_', AptNumber))...
DDL:
SELECT * INTO crewWork FROM (VALUES
-- data from question, with a couple changes to demonstrate problems with the accepted answer
-- https://stackoverflow.com/q/8666295/1175496
--FloorNumber AptNumber WorkType simTime
(1, 1, 12, 10 ),
-- (1, 1, 12, 25 ), -- original
(2, 1, 12, 25 ), -- new, changing FloorNumber 1->2->1
(1, 1, 13, 35 ),
(1, 1, 13, 47 ),
(1, 2, 12, 52 ),
(1, 2, 12, 59 ),
(1, 2, 13, 68 ),
(1, 1, 14, 75 ),
(1, 4, 12, 79 ),
-- (1, 4, 12, 89 ), -- original
(1, 1, 12, 89 ), -- new , changing AptNumber 4->1->4 ges)
(1, 4, 13, 92 ),
(1, 4, 14, 105 ),
(1, 3, 12, 115 ),
...
DML:
;
WITH groupedWithConcats as (SELECT
*,
CONCAT(AptGroup,'_', AptNumber) as AptCombo,
CONCAT(FloorGroup,'_',FloorNumber) as FloorCombo
-- SQL SERVER doesnt have TEMPORARY keyword; Postgres doesn't understand # for temp tables
-- INTO TEMPORARY groupedWithConcats
FROM
(
SELECT
-- the columns shown in Andriy's answer:
-- https://stackoverflow.com/a/8667477/1175496
ROW_NUMBER() OVER ( ORDER BY simTime) as RN,
-- AptNumber
AptNumber,
ROW_NUMBER() OVER (PARTITION BY AptNumber ORDER BY simTime) as RN_Apt,
ROW_NUMBER() OVER ( ORDER BY simTime)
- ROW_NUMBER() OVER (PARTITION BY AptNumber ORDER BY simTime) as AptGroup,
-- FloorNumber
FloorNumber,
ROW_NUMBER() OVER (PARTITION BY FloorNumber ORDER BY simTime) as RN_Floor,
ROW_NUMBER() OVER ( ORDER BY simTime)
- ROW_NUMBER() OVER (PARTITION BY FloorNumber ORDER BY simTime) as FloorGroup
FROM crewWork
) grouped
)
-- if you want to see how the groupings work:
-- SELECT * FROM groupedWithConcats
-- otherwise just run this query to see the counts of "changes":
SELECT
COUNT(DISTINCT AptCombo)-1 as CountAptChangesWithConcat_Correct,
COUNT(DISTINCT AptGroup)-1 as CountAptChangesWithoutConcat_Wrong,
COUNT(DISTINCT FloorCombo)-1 as CountFloorChangesWithConcat_Correct,
COUNT(DISTINCT FloorGroup)-1 as CountFloorChangesWithoutConcat_Wrong
FROM groupedWithConcats;
ALTERNATIVE ANSWER
The accepted-answer may eventually get updated to remove the mistake. If that happens I can remove my warning but I still want leave you with this alternative way to produce the answer.
My approach goes like this: "check the previous row, if the value is different in previous row vs current row, then there is a change". SQL doesn't have idea or row order functions per se (at least not like in Excel for example; )
Instead, SQL has window functions. With SQL's window functions, you can use the window function RANK plus a self-JOIN technique as seen here to combine current row values and previous row values so you can compare them. Here is a db<>fiddle showing my approach, which I pasted below.
The intermediate table, showing the columns which has a value 1 if there is a change, 0 otherwise (i.e. FloorChange, AptChange), is shown at the bottom of the post...
DDL:
...same as above...
DML:
;
WITH rowNumbered AS (
SELECT
*,
ROW_NUMBER() OVER ( ORDER BY simTime) as RN
FROM crewWork
)
,joinedOnItself AS (
SELECT
rowNumbered.*,
rowNumberedRowShift.FloorNumber as FloorShift,
rowNumberedRowShift.AptNumber as AptShift,
CASE WHEN rowNumbered.FloorNumber <> rowNumberedRowShift.FloorNumber THEN 1 ELSE 0 END as FloorChange,
CASE WHEN rowNumbered.AptNumber <> rowNumberedRowShift.AptNumber THEN 1 ELSE 0 END as AptChange
FROM rowNumbered
LEFT OUTER JOIN rowNumbered as rowNumberedRowShift
ON rowNumbered.RN = (rowNumberedRowShift.RN+1)
)
-- if you want to see:
-- SELECT * FROM joinedOnItself;
SELECT
SUM(FloorChange) as FloorChanges,
SUM(AptChange) as AptChanges
FROM joinedOnItself;
Below see the first few rows of the intermediate table (joinedOnItself). This shows how my approach works. Note the last two columns, which have a value of 1 when there is a change in FloorNumber compared to FloorShift (noted in FloorChange), or a change in AptNumber compared to AptShift (noted in AptChange).
floornumber
aptnumber
worktype
simtime
rn
floorshift
aptshift
floorchange
aptchange
1
1
12
10
1
0
0
2
1
12
25
2
1
1
1
0
1
1
13
35
3
2
1
1
0
1
1
13
47
4
1
1
0
0
1
2
12
52
5
1
1
0
1
1
2
12
59
6
1
2
0
0
1
2
13
68
7
1
2
0
0
Note instead of using the window function RANK and JOIN, you could use the window function LAG to compare values in the current row to the previous row directly (no need to JOIN). I don't have that solution here, but it is described in the Wikipedia article example:
Window functions allow access to data in the records right before and after the current record.
If I am not missing anything, you could use the following method to find the number of changes:
determine groups of sequential rows with identical values;
count those groups;
subtract 1.
Apply the method individually for AptNumber and for FloorNumber.
The groups could be determined like in this answer, only there's isn't a Seq column in your case. Instead, another ROW_NUMBER() expression could be used. Here's an approximate solution:
;
WITH marked AS (
SELECT
FloorGroup = ROW_NUMBER() OVER ( ORDER BY simTime)
- ROW_NUMBER() OVER (PARTITION BY FloorNumber ORDER BY simTime),
AptGroup = ROW_NUMBER() OVER ( ORDER BY simTime)
- ROW_NUMBER() OVER (PARTITION BY AptNumber ORDER BY simTime)
FROM crewWork
)
SELECT
FloorChanges = COUNT(DISTINCT FloorGroup) - 1,
AptChanges = COUNT(DISTINCT AptGroup) - 1
FROM marked
(I'm assuming here that the simTime column defines the timeline of changes.)
UPDATE
Below is a table that shows how the distinct groups are obtained for AptNumber.
AptNumber RN RN_Apt AptGroup (= RN - RN_Apt)
--------- -- ------ ---------
1 1 1 0
1 2 2 0
1 3 3 0
1 4 4 0
2 5 1 4
2 6 2 4
2 7 3 4
1 8 5 => 3
4 9 1 8
4 10 2 8
4 11 3 8
4 12 4 8
3 13 1 12
3 14 2 12
3 15 3 12
1 16 6 10
… … … …
Here RN is a pseudo-column that stands for ROW_NUMBER() OVER (ORDER BY simTime). You can see that this is just a sequence of rankings starting from 1.
Another pseudo-column, RN_Apt contains values produces by the other ROW_NUMBER, namely ROW_NUMBER() OVER (PARTITION BY AptNumber ORDER BY simTime). It contains rankings within individual groups of identical AptNumber values. You can see that, for a newly encountered value, the sequence starts over, and for a recurring one, it continues where it stopped last time.
You can also see from the table that if we subtract RN from RN_Apt (could be the other way round, doesn't matter in this situation), we get the value that uniquely identifies every distinct group of same AptNumber values. You might as well call that value a group ID.
So, now that we've got these IDs, it only remains for us to count them (count distinct values, of course). That will be the number of groups, and the number of changes is one less (assuming the first group is not counted as a change).
add an extra column changecount
CREATE TABLE crewWork(
FloorNumber int, AptNumber int, WorkType int, simTime int ,changecount int)
increment changecount value for each updation
if want to know count for each field then add columns corresponding to it for changecount
Assuming that each record represents a different change, you can find changes per floor by:
select FloorNumber, count(*)
from crewWork
group by FloorNumber
And changes per apartment (assuming AptNumber uniquely identifies apartment) by:
select AptNumber, count(*)
from crewWork
group by AptNumber
Or (assuming AptNumber and FloorNumber together uniquely identifies apartment) by:
select FloorNumber, AptNumber, count(*)
from crewWork
group by FloorNumber, AptNumber