SQL group numbers that are 'close' together using a threshold value - sql

Consider the table:
id value
1 2
2 4
3 6
4 9
5 10
6 12
7 19
8 20
9 22
I want to group them by a threshold value so that I can find values that are 'close' together.
To do this I want another column that groups these numbers together. For this example use 2 as the
threshold. The result should be like this. It does not matter what is used as the group label, just
as long as it makes it easy to query later.
id value group_label
1 2 A
2 4 A
3 6 A
4 9 B
5 10 B
6 12 B
7 19 C
8 20 C
9 22 C

I couldn't get the version using lag() to work but here's a mysql query using variables
select id, value,
(case
when (value - #value) > 2
then #groupLabel := #groupLabel + 1
else #groupLabel
end) groupLabel, #value := value
from data cross join (
select #value := -1, #groupLabel := 0
) t1
order by value
SQLFiddle
Update
Here's a query using lag
select t1.id, t1.value, count(t2.id)
from data t1 left join (
select id, value,
case when
(value - lag(value) over (order by value)) > 2
then 1 else 0
end groupLabel
from data
) t2 on t2.groupLabel = 1
and t2.id <= t1.id
group by t1.id, t1.value
order by t1.value
SQLFiddle

Related

Countif statement in Postgresql

How can I use countif statement in PostgreSQL?
max(COUNTIF(t1.A1:C10,t2.a1),COUNTIF(t1.A1:C10,t2.b1),COUNTIF(t1.A1:C10,t2.c1))
I have table1 which is more then a million rows
a
b
c
M5
16
27
31
3
7
27
and table2 more then 100 rows including different dates after column c
a
b
c
10
15
16
30
40
50
60
70
80
16
18
37
5
12
16
8
31
28
11
12
13
7
9
31
2
7
21
20
16
27
8
12
17
2
8
14
3
14
15
The outcome should be something like this
a
b
c
M5
16
27
31
3
3
7
27
2
Tried the below query but the outcome is not correct
UPDATE table1 SET m5 = greatest(
case When a in(select unnest(array[a,b,c]) from (select * from table2 order by date DESC limit 10) foo) then 1 else 0 END,
case When b in(select unnest(array[a,b,c]) from (select * from table2 order by date DESC limit 10) foo) then 1 else 0 END,
case When c in(select unnest(array[a,b,c]) from (select * from table2 order by date DESC limit 10) foo) then 1 else 0 END)
Assuming your columns are fixed and predictable, I think you could put all possible table values into a single column and then do counts for each occurrence:
with exploded as (
select a from table2
union all
select b from table2
union all
select c from table2
)
select a, count (*) as count
from exploded e
group by a
So for example, the value 7 occurs twice (which would be reflected in this output).
From there, you can just do the updates from the CTE:
with exploded as (
select a from table2
union all
select b from table2
union all
select c from table2
),
counted as (
select a, count (*) as count
from exploded e
group by a
)
update table1 t
set m5 = greatest (ca.count, cb.count, cc.count)
from
counted ca,
counted cb,
counted cc
where
t.a = ca.a and
t.b = cb.a and
t.c = cc.a
The only issue I see is if one of the values does not come up (the inner join fails), but in your example that doesn't seem to happen.
If it is possible, I would think that could be resolved with one more CTE to fill in missing values from table1 in the set of possible occurrences.

SQL: How to join two columns in a specific way?

I am working with an Oracle Database and I am new to SQL in general.
I have a table with data and month columns. After filtering the data I have just a few rows left. But I want to get two columns: 1-st column with 12 months listed (1,2,3,4,5,6,7,8,9,10,11,12) and second column with values from original data (if exist) or zeroes.
F.e.: Original data:
MONTH VALUE
9 96
What I want:
MONTH VALUE
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 96
10 0
11 0
12 0
I have already tried to use join and union all functions but it didn't work out.
First generate a sequence of 12 months number then use left join
select monthNo, coalesce(Value,0) as value from
(
SELECT 1 MonthNo
FROM dual
CONNECT BY LEVEL <= 12
)A left join originaltable b on A.monthNo=b.month
is this what are you looking for?
WITH tab AS(SELECT LEVEL AS m , null as value FROM DUAL CONNECT BY LEVEL <= 12)
, tab2 AS(SELECT 9 as m, 96 as VALUE FROM DUAL)
select t1.m
,coalesce(t2.value,0) as value
from tab t1
left join tab2 t2 on t1.m = t2.m
order by 1
Bro enjoy...
select months.month ,original_data.VALUE
from original_data
Right JOIN (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) months(month) on
months.month = original_data.MONTH
order by months.month --optional

SQL query count (recursive)

I have the following table on my database which contains some transactions for which I need to calc points and rewards.
Every time a TxType A occurs I should record 10 points.
Then I have to subtract from these points the value of the PP column every time a TxType B occurs.
When the calculation goes to zero a reward is reached.
ID TxType PP
1 A 0
2 B 2
3 B 1
4 B 1
5 B 1
6 B 3
7 B 1
8 B 1
9 A 0
10 B 4
11 B 3
12 B 2
13 B 1
14 A 0
15 B 2
I have created the sql query to calc points as follow
SELECT SUM(
CASE
WHEN TxType = 'A' THEN 10
WHEN TxType = 'B' THEN (PP * -1)
END)
FROM myTable
This query return the value of 8, which is exactly the number of points based on the sample data.
How do I calculate the rewards occurred (2 in the given example)?
thanks for helping
One way to do the calculation (in SQL Server 2008) using a correlated subquery:
select t.*,
(select sum(case when TxType = 'A' then 10
when TxType = 'B' then PP * -1
end)
from mytable t2
where t2.id <= t.id
) as TheSum
from mytable t;
You can then apply the logic of what happens when the value is 0. In SQL Server 2012, you could just use a cumulative sum.
To complete Gordon Linoff's the answer, you just need to count the records where TheSum is 0 to get how many rewards occurred:
SELECT COUNT(1)
FROM (
SELECT ID,
TxType,
PP,
( SELECT SUM(CASE TxType WHEN 'A' THEN 10 WHEN 'B' THEN -PP END)
FROM #myTable t2
WHERE t2.id <= t1.id
) AS TheSum
FROM #myTable t1
) Result
WHERE TheSum = 0

Get next minimum, greater than or equal to a given value for each group

given the following Table1:
RefID intVal SomeVal
----------------------
1 10 val01
1 20 val02
1 30 val03
1 40 val04
1 50 val05
2 10 val06
2 20 val07
2 30 val08
2 40 val09
2 50 val10
3 12 val11
3 14 val12
4 10 val13
5 100 val14
5 150 val15
5 1000 val16
and Table2 containing some RefIDs and intVals like
RefID intVal
-------------
1 11
1 28
2 9
2 50
2 51
4 11
5 1
5 150
5 151
need an SQL Statement to get the next greater intValue for each RefID and NULL if not found in Table1
following is the expected result
RefID intVal nextGt SomeVal
------------------------------
1 11 20 val01
1 28 30 val03
2 9 10 val06
2 50 50 val10
2 51 NULL NULL
4 11 NULL NULL
5 1 100 val14
5 150 150 val15
5 151 1000 val16
help would be appreciated !
Derived table a retrieves minimal values from table1 given refid and intVal from table2; outer query retrieves someValue only.
select a.refid, a.intVal, a.nextGt, table1.SomeVal
from
(
select table2.refid, table2.intval, min (table1.intVal) nextGt
from table2
left join table1
on table2.refid = table1.refid
and table2.intVal <= table1.intVal
group by table2.refid, table2.intval
) a
-- table1 is joined again to retrieve SomeVal
left join table1
on a.refid = table1.refid
and a.nextGt = table1.intVal
Here is Sql Fiddle with live test.
You can solve this using the ROW_NUMBER() function:
SELECT
RefID,
intVal,
NextGt,
SomeVal,
FROM
(
SELECT
t2.RefID,
t2.intVal,
t1.intVal AS NextGt,
t1.SomeVal,
ROW_NUMBER() OVER (PARTITION BY t2.RefID, t2.intVal ORDER BY t1.intVal) AS rn
FROM
dbo.Table2 AS t2
LEFT JOIN dbo.Table1 AS t1 ON t1.RefID = t2.RefID AND t1.intVal >= t2.intVal
) s
WHERE
rn = 1
;
The derived table matches each Table2 row with all Table1 rows that have the same RefID and an intVal that is greater than or equal to Table2.intVal. Each subset of matches is ranked and the first row is returned by the main query.
The nested query uses an outer join, so that those Table2 rows that have no Table1 matches are still returned (with nulls substituted for the Table1 columns).
Alternatively you can use OUTER APPLY:
SELECT
t2.RefID,
t2.intVal,
t1.intVal AS NextGt,
t1.SomeVal
FROM
dbo.Table2 AS t2
OUTER APPLY
(
SELECT TOP (1)
t1.intVal
FROM
dbo.Table1 AS t1
WHERE
t1.RefID = t2.RefID
AND t1.intVal >= t2.intVal
ORDER BY
t1.intVal ASC
) AS t1
;
This method is arguably more straightforward: for each Table2 row, get all matches from Table1 based on the same set of conditions, sort the matches in the ascending order of Table1.intVal and take the topmost intVal.
This can be done with a join, group by, and a case statement, and a trick:
select t1.refid, t2.intval,
min(case when t1.intval > t2.intval then t1.intval end) as min_greater_than_ref,
substring(min(case when t1.intval > t2.intval
then right('00000000'+cast(t1.intval as varchar(255)), 8)+t1.SomeVal)
end)), 9, 1000)
from table1 t1 left join
table2 t2
on t1.refid = t2.refid
group by t1.refid, t2.intval
SO, the trick is to prepend the integer value to SomeValue, zero-padding the integer value (in this case to 8 characters). You get something like: "00000020val01". The minimum on this column is based on the minimum of the integer. The final step is to extract the value.
For this example, I used SQL Server syntax for the concatenation. In other databases you might use CONCAT() or ||.

Query results with no reverse

Ive got table:
UserA,
UserB,
numberOfConnections
I would like to write query which returns me only rows that has no reverse I mean or example :
for data :
1 2 10
1 3 10
1 5 10
1 6 10
2 6 10
2 5 10
5 1 10
5 2 10
3 1 10
it should return
1 2 10
1 3 10
1 5 10
1 6 10
2 6 10
2 5 10
rows:
5 1 10
5 2 10
3 1 10
arent valid because there are already corresponding
1 5 10
2 5 10
3 1 10
thanks for help
bye
This will do what you want
SELECT mt1.UserA, mt1.UserB, mt1.numberOfConnections
FROM MyTable mt1
LEFT OUTER JOIN MyTable mt2 ON mt1.UserA = mt2.UserB
AND mt1.UserB = mt2.UserA
WHERE mt2.UserA IS NULL
OR mt1.UserA < mt2.UserA
Why not add UserA < UserB in where clause. ?
Use Updated
A- Result No Reverse
Select UserA, UserB, numberOfConnections
From TName
Where ID Not IN(
SELECT t1.ID
FROM TName As t1 INNER JOIN TName As t2 ON (t1.UserA = t2.UserB) AND (t1.UserB = t2.UserA))
B- Result Reverse Only Repeted
Select UserA, UserB, numberOfConnections
From TName
Where ID IN(
SELECT t1.ID
FROM TName As t1 INNER JOIN TName As t2 ON (t1.UserA = t2.UserB) AND (t1.UserB = t2.UserA))
C- Result Reverse Only Not Repeted
Select UserA, UserB, numberOfConnections
From TName
Where ID IN(
SELECT t1.ID
FROM TName As t1 INNER JOIN TName As t2 ON (t1.UserA = t2.UserB) AND (t1.UserB = t2.UserA) and (t1.UserA<t1.UserB))
D- Result No Reverse and Not Repeted Reverse
A
Union
C
with t1 as (
select col1 ,col2,
row_number()
over(partition by case when col1>col2 then col1+col2 else col2+col1 end
order by case when col1>col2 then col1+col2 else col2+col1 end) as rownum
from table_1 )
select * from t1 where rownum =1
if your data type of col1 and col2 are not varchar, just cast them
you can replace the row_number by group by