An sql statement to select the closest values - sql

In the table below, that I'll call TableA are two numerical columns. I need to create a Select statement whereby the value of B is specified. Either one or two rows are returned. Not sure if this can be done in a single sql statement. If a row exists where the value of B matches, then just that row is returned. If the value of B is between two values of B that are closest to its value, both values are returned. If a value exists that is larger than it but no value exists that is smaller than it, than the larger value is returned. If no larger value exists but a smaller one does, than the row with the smaller value is returned. Here are some examples. It would be nice if the sql worked in sqlite:
A B
50 400
10 200
30 100
40 800
20 500
B = 10
A B
30 100
----------
B = 250
A B
10 200
50 400
----------
B = 100
A B
30 100
----------
B = 410
A B
50 400
20 500
----------
B = 900
A B
40 800

SELECT * FROM A WHERE B = 10
UNION
SELECT * FROM A WHERE B = (SELECT MAX(B) FROM A WHERE B < 10)
UNION
SELECT * FROM A WHERE B = (SELECT MIN(B) FROM A WHERE B > 10);
See it working live in an sqlfiddle.

SELECT * FROM TableA WHERE B = (SELECT MAX(B) FROM TableA WHERE B <= 10)
UNION
SELECT * FROM TableA WHERE B = (SELECT MIN(B) FROM TableA WHERE B >= 10)

Related

How to find records where at least a single record among multiple records having same column values matches with other table but one or more aren't?

I have tables
Table_A
Rec_Id
Cust_Id
Act_Cp
1
10
x
2
11
y
2
12
z
3
13
x
3
14
y
3
15
z
4
16
z
Table_B
sf_id
sf_detail
x
good
y
bad
I want to find the records where at least one of the record of a rec_id is present in table_B but one or more aren't.
the condition is act_cp = sf_id
Somehow my expected output will be:
rec_id
cust_id
act_cp
2
12
z
3
15
z
In the above output , I would not need the row
rec_id
cust_id
act_cp
4
16
z
because there is only a single record with rec_id =4
Also,it would be great,if joins are not used since I don't want any data from table_b just need to check. So , the query can use exists, not exists statement
SELECT *
FROM table_a t1
WHERE EXISTS (SELECT SF_ID FROM table_b t2 WHERE t1.act_cp = t2.sf_id)
I tried doing:
SELECT *
FROM Table_A AS t1
WHERE Rec_Id IN
(SELECT Rec_Id FROM Table_B GROUP BY Rec_Id HAVING COUNT(*) > 1)
AND NOT EXISTS
(SELECT Sf_Id FROM Table_B AS t2 WHERE t1.Act_Cp = t2.Sf_Id)
But this will work only for the above data.
If table_A is changed to the below, the query will not work.
Rec_Id
Cust_Id
Act_Cp
1
10
x
2
11
y
2
12
z
3
13
x
3
14
y
3
15
z
4
16
z
5
17
z
5
18
z
5
19
z
I would like to make adjustments that at least a single record from multiple rec_id should match so the output would be similar to the previous one.
rec_id
cust_id
act_cp
2
12
z
3
15
z
What you might need is an outer join along with a window function to count the occurence of the matching whenever grouped by Rec_Id column such as
WITH ab AS
(
SELECT a.*, b.*,
SUM( CASE WHEN sf_id IS NOT NULL THEN 1 ELSE 0 END )
OVER ( PARTITION BY Rec_Id ) AS cnt
FROM a
LEFT JOIN b
ON act_cp = sf_id
)
SELECT Rec_Id,Cust_Id,Act_Cp
FROM ab
WHERE sf_id IS NULL
AND cnt > 0
Demo on MySQL DB
What I understand is that you need to get records that act_cp is not equal to "x" and "y" but "z" only. So remove any rec_id's that have x-y in any row.
First, I would pick rec_id's that include x and y's.
SELECT rec_id, cust_id
FROM table_a
WHERE act_cp IN (SELECT sf_id FROM table_b)
This will return:
Rec_Id
Cust_Id
Act_Cp
1
10
x
2
11
y
2
12
z
3
13
x
3
14
y
3
15
z
Now time to get the remaining row that you requested
SELECT rec_id, cust_id, act_cp
FROM table_a
WHERE rec_id NOT IN
(SELECT rec_id
FROM table_a
WHERE act_cp IN (SELECT sf_id FROM table_b));
I assume this is a long way but you get the results. The less expensive method is to use left joins - I know you stated "no joins" but it might be good for future reference
SELECT a.rec_id, a.cust_id, a.act_cp
FROM table_a a
LEFT JOIN table_b b
ON a.act_cp = b.sf_id
WHERE act_cp IS NULL;
So with this, you still won't return any data from table b and get your requested data.

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.

Count elements in table a that have two exactly matching elements in table b

I have two tables and need to get a count of all entries from table A that have two specific matches in table B. Table B has tables A's Id as a foreign key.
Table a
ID Name
1 Foo
2 Bar
3 John
4 Jane
Table b
aID Value
1 12
1 12
2 8
3 8
3 12
4 12
4 8
I now need a count of all names in table A that have both value 8 AND 12 in table B at least once.
SELECT COUNT(*) FROM a join b on a.id = b.aId where b.value = 8 and b.value = 12
gets me 0 results. The correct result should be 2 (John and Jane).
edit:
Obviously, #Larnu is correct and 8 will never be 12.
Also, I should have clarified that there can be two or more of a single value in table B for any table A id, but none of the other (e.g. 8 twice but no 12). I updated the table to reflect that.
You can use EXISTS and HAVING:
SELECT COUNT(*) FROM a
WHERE EXISTS(SELECT b.aID FROM b
WHERE a.ID = b.aID
GROUP BY b.aID
HAVING COUNT(*) = 2)
If you want specifically value = 8 or 12 then add AND b.value IN(8,12) to the inner query
A subquery to get the number of times 8 and 12 appear for each row will do the trick:
select count(id) from
(select id, sum(case when b.Value = 8 then 1 else 0 end) as ct8,
sum(case when b.Value = 12 then 1 else 0 end) as ct12
from a inner join b on a.id = b.aID
group by a.id) as t
where ct8 >= 1 and ct12 >=1
Fiddle
Joining is not the answer here. You need a WHERE clause that includes a correlated subquery that checks your condition using COUNT() or EXISTS(). One of the following should do.
SELECT COUNT(*) FROM A
WHERE (SELECT COUNT(*) ​FROM B ​WHERE B.aID = A.ID ​AND B.VALUE IN (8, 12)) = 2
SELECT COUNT(*) FROM A
WHERE EXISTS(SELECT * FROM B WHERE B.aID = A.ID AND B.VALUE = 8)
AND EXISTS(SELECT * FROM B WHERE B.aID = A.ID AND B.VALUE = 12)

Postgresql query with left join and having

Postgresql 9.1: I have a query that must return the values of a second table only if the aggregate function SUM of two columns is greater than zero.
This is the data:
Table a
id
---
1
2
3
Table b
id fk(table a)
---------------
1 1
2 null
3 3
Table c
id fk(table b) amount price
-----------------------------------
1 1 1 10 --positive
2 1 1 -10 --negative
3 3 2 5
As you can see, table b has some ids from table a, and table c can have 1 or more references to table b, table c is candidate to be retrieved only if the sum(amount * price ) > 0.
I wrote this query:
SELECT
a.id, b.id, SUM(c.amount * c.price) amount
FROM
tablea a
LEFT JOIN
tableb b ON b.fk = a.id
LEFT JOIN
tablec c ON c.fk = b.id
GROUP BY
a.id, b.id
HAVING
SUM(c.amount * c.price) > 0
But this query is not retrieving all rows from table a just the row 1 and I need the two rows. I understand this is happening because of the HAVING clause but I don't know how to rewrite it.
Expected result
a b sum
------------------
1 null null -- the sum of 1 * 10 (rows 1 and two) = 0 so its not retrieved.
2 null null -- no foreign key in second table
3 3 10 -- the sum of 2 * 5 (row 3) > 0 so it's ok.
Try this:
SELECT A.ID, B.ID, C.ResultSum
FROM TableA A
LEFT JOIN TableB B ON (B.FK = A.ID)
LEFT JOIN (
SELECT FK, SUM(Amount * Price) AS ResultSum
FROM TableC
GROUP BY FK
) C ON (C.FK = B.ID) AND (ResultSum > 0)
See demo here.

SQL to solve this

I have the following data in a table I'll call TableA:
ID Status Date
5 0 1000
20 0 900
10 1 800
30 1 700
4 1 600
8 0 500
22 1 400
1 1 300
3 0 200
The records are sorted by Date descendingly. I want to get only those records where Status is equal to 1 BUT only up to the first record where the Status is no longer 1. So in the sample data, records with ID: 10,30,4 would be selected but but 22 and 1 would not be because ID 8 appears and separates the sets. Preferrably the SQL should run in Sqlite. The result for this sample data should return:
ID Status Date
10 1 800
30 1 700
4 1 600
EDIT
I replaced the ID values with random values and changed the date from TEXT to Integer.
I suggest
select * from tableA a1 where a1.status = 1 and not exists
(select 1 from tableA a2 where a2.status = 0 and a2.date > a1.date and a2.date <
(select max(date) from tableA a3 where a3.status = 1
)
)
Doubly nested subquery. Select rows where the status is 1 that have no rows before them with (status is 0 and that are after the earliest row where status is 1).
No idea how efficient this is.
Here you go:
SELECT *
FROM
TableA A
INNER JOIN (
SELECT *
FROM TableA S
WHERE S.Status = 1
ORDER BY S.Date DESC
LIMIT 1
) S ON A.Date <= S.Date
WHERE
A.Status = 1
AND A.Date > (
SELECT E.Date
FROM TableA E
WHERE
E.Status = 0
AND S.Date > E.Date
ORDER BY Date DESC
LIMIT 1
)
;
See a Live Demo at SQL Fiddle
This should be pretty efficient because of the LIMIT clauses. If there are many rows in the table it theoretically won't be scanning them all--but big disclaimer: I don't work with sqlite much at all.
this is not tested, but will give an idea.
It's for MSSQL and uses subqueries; I dont know if it works for sqlite.
select RowNumber() r, *
from (select * from TableA where status = 1), (select top 1 id from TableA where status = 1) diff
where id - r = diff - 1