Passing values to a query that may be null - mysql

I'm trying to make a filter section for a website that will return objects from the DB.
SELECT *
FROM users
WHERE type = "1"
AND age < 50
AND age > 20
When entering this query, some users may not want to set a type, just filter it by age? Is there any sort of wild card i can pass into the query so it returns all if it not been defined by the user?
The type can either be 1, 2 or 3. if this is not passed into it, i want it to return all users between the age of 20 and 50, and then all types?

These could be two possible solutions, first is simple if you want all records when age between 50 and 20. Second query will return records if type=1 and age between 50 and 20,
SELECT *
FROM users
WHERE age BETWEEN 20 AND 50
OR
SELECT * FROM users
WHERE
IF(type == 1)
THEN
type = 1
ELSE
1=1
END IF
AND age BETWEEN 20 AND 50

SELECT * FROM USERS WHERE AGE BETWEEN 20 AND 50 AND COALESCE(TYPE,'1')=1

Try this query :
select * from table_name where age between 20 and 50 AND type is null

Related

SQL query on messages table returns not all results. Strange behavior

Can someone help. I have strange behavior on SQL query to "messages" table.
It supposed to return MAX(id)'s for each user contacted target user.
Table is simple:
id(int, ai) | from(int) | dest(int) | text(txt) | time(int) | msg_status(int)
USERS table have only 5 test users.
MSG table have about 40 messages.
When I query most of user ids(1, 2, 3, 4) - I receive normal result.
When I query one specific user No.5- I receive ONE less result.
The query is:
SELECT MAX(`id`) FROM `msg` WHERE `from` = '5' OR `dest` = '5'
GROUP BY (IF(`from` > `dest`, `from`, `dest`)), (IF(`from` < `dest`, `dest`, `from`));
For most users it gives normal result. For example for user 1 I have:
MAX(id) 37, 30, 33, 36
And it is OK as user No.1 have conversation messages with all other 4 users.
But for user No.5 I have:
MAX(id) 36
Thus this is not correct. As user No.5 have last messages as described here:
id from dest text
35 5 2 hellp
36 5 1 hi there
So there is one less result, as it have to be something like:
MAX(id) 35, 36
But it is not.
Can someone suggest what is wrong?
UPD.
Simplifying the query:
SELECT * FROM `msg` WHERE `id` IN (SELECT MAX(`id`) FROM `msg`
WHERE `from` = '5' OR `dest` = '5' GROUP BY `from`, `dest`);
I receive result:
id from desr text
32 1 5 test
35 5 2 hello
36 5 1 test2
So oroginal query have to produce 35 and 36 result, thus giving 36 only...
There's a logic error in the GROUP BY expressions:
GROUP BY
(IF(`from` > `dest`, `from`, `dest`)),
(IF(`from` < `dest`, `dest`, `from`));
If from > dest, then this is equivalent to GROUP BY from, from; if from < dest, then this is GROUP BY dest, dest. For the two example rows with from = 5, they're grouped by from, and thus in the same group, and thus MAX(id) is 36, with no 35 in the results.
In contrast, ID 1 will be the minimum when compared to any other user ID, so when querying for ID 1, the query will group by the other IDs, guaranteeing they remain separate groups. That is why the query works for ID 1.
To avoid this error, it's better to use the Greatest and Least functions:
GROUP BY Greatest(`from`, `dest`), Least(`from`, `dest`)
The 2 GROUP BY statements should return the same value (5) for both Id 35 and 36 so MAX(id) will return 36 - which is what you are getting.
Your SQL matches your result so it all seems to be working. If the result is not what you want then you’ll need to change your SQL - and if you want help with that then you’ll need to explain what the logic is that would return IDs 35 and 36

SQL: how to produce table of all the individual ID's that had more than 2 occurrences within 24 hours of each other

I have a table as shown below
User ID
Payment ID
DateTime
Amount
UID1
12dw4r434t
19/08/2020 13:40:12
10
UID2
k2dw86774z
19/08/2020 14:30:52
5
UID3
5hjs982835
17/08/2020 09:56:08
7
UID1
hg19283jdg
20/08/2020 07:59:33
12
UID1
2563ghmn77
20/08/2020 08:10:22
54
UID2
999gegh77d
19/08/2020 17:11:37
67
UID2
mnnnhsgdje
20/08/2020 19:18:55
67
UID1
qccc356njd
20/08/2020 16:10:11
87
UID3
mmk0999111
18/08/2020 05:16:29
4
UID3
yyy63hgd72
18/08/2020 05:25:44
4
I want to be able to produce a table/list of all the User IDs that have at least 3 (more than 2) occurrences within 24 hours of each other.
The result for the above data would be:
User ID
UID1
UID3
To be clear, I want the 24 hour period to be rolling. I am not looking for the results over a fixed date range.
ALTERNATIVE SOLUTION OUTPUT
Another output solution that would satisfy my need would be to produce a table with an extra column (Count) added to the table above. For every row, the Count column counts how many transactions within the whole table are within 24 hours (and for the same User ID)of the transaction for the current row . I have easily managed to do this using COUNTIF in Excel, but cannot get it working in SQL. Below is how I tried to do this:
SELECT
a."User Id",
a."Payment Id",
a."created_at",
a."Amount",
b.m_count
FROM "Payment Summary Table" AS a
INNER JOIN( SELECT
"User Id",
"created_at",
COUNT(*) AS m_count
FROM "Payment Summary Table"
GROUP BY "Merchant",
"amount"
) AS b ON a."User Id" = b."User Id"
AND b."created_at" <= dateadd (hour, 24, a."created_at")
You can get the first of the three using lead() and date arithmetic:
select pst.*
from (select pst.*,
lead(created_at, 2) over (partition by user_id order by created_at) as created_at_2
from payment_summary_table pst
) pst
where created_at_2 < dateadd(hour, 24, created_at);
This looks at the payment 2 rows ahead for the user. If it is within 24 hours, then the three payments are within 24 hours.
If you just want the users, replace the select with:
select distinct user_id

Why does RAND sometimes produce multiple results in a MySQL call?

I have table agent which includes 13 agent IDs and agent names.
agent_id|agent_name|
--------|----------|
1|Jack |
2|Jill |
3|Jo |
...
When I run this query...
select agent_name from agent where agent_id = (FLOOR( 1 + RAND( ) * 13 ))
...MySQL sometimes returns 0 names and sometimes returns many names. Since (FLOOR( 1 + RAND( ) * 13 )) on its own always seems to return a single, non-zero integer, I would expect to get a single name back, but this is not the case. On testing the above query, the following number of names are returned on each execution;
Execution | Total Names Returned
1 | 3
2 | 2
3 | 1
4 | 0
5 | 1
6 | 1
7 | 0
8 | 0
9 | 1
10 | 4
Clearly, when one runs...
select agent_name from agent where agent_id = 3
...the same, single, name is returned each time.
I see that the docs explain that
RAND() in a WHERE clause is evaluated for every row (when selecting from one table) or combination of rows (when selecting from a multiple-table join). Thus, for optimizer purposes, RAND() is not a constant value and cannot be used for index optimizations
I am not sure why this would mean that a single call would return many rows.
If I add LIMIT 1
select * from agent a where agent_id = (FLOOR( 1 + RAND() * 13 )) limit 1
...then the query sometimes returns NULL.
Questions
Why does RAND return different numbers of records?
What is the proper way to return a single random agent?
As the documentation says, it is evaluated for each row. It means, the following:
MySQL gets all rows
For each row, executes RAND() and if RAND() == id, return the row
For example
Get all rows
ID = 1, RAND() = 3, no return
ID = 2, RAND() = 2, return
ID = 3, RAND() = 4, no return
ID = 4, RAND() = 4, return
In this case, you have two results.
If you want to get random agent, maybe better approach would be
SELECT * FROM `agent` ORDER BY RAND() LIMIT 1
I assume that your first query is sometimes returning multiple records because RAND() is being evaluted for each record in the query. If you just want a single random agent, then use:
SELECT agent_name
FROM agent
ORDER BY RAND()
LIMIT 1;
To further explain your current observations, the following expression is being evaluated once per each agent record:
(FLOOR( 1 + RAND( ) * 13 ))
Imagine that for each record, the above happens, by chance, to equal the agent_id. If so, then your first query would return all 13 records. By the way, the documentation link you cited basically says this.

count records grouped into ranges

I have a table in my db called students with a column age along with other columns. I need to count students in each age range e.g. give me count of students with age between 0-5, 6-10,11-15 onward. Can I get this with single query or do i need to use BETWEEN clause in loop.
Thanks
EDIT:
This can also be taken as employee-salary relation. It can be changed to count employees with salaries in different ranges e.g. 1000k to 1500k etc.
i dont think there is a function that will group the ages into your ranges or buckets. so you'll have to do that manually and then group the data.
select
case when age < 5 then '0-5'
when age < 10 then '6-10'
when age < 15 then '11-15'
...
...
end as agerange
count(studentID)
from students
group by agerange
You can do it with a case and between the given ages (mine is just for an example), and then group by that.
select case
when age between 0 and 20 then ' 0-20'
when age between 21 and 41 then ' 21-41'
when age between 42 and 62 then ' 42-62'
when age between 63 and 83 then ' 63-83'
when age between 84 and 104 then ' 84-104'
end as `range`, count(*) as `users`
from users group by `range`
I haven't been able to test it, but it should work :),else post the error and we try to fix it from there

how to generate subsets from a query in postgres?

I need subsets of tuples for a given query. For example , if I need to find list of all employees who are older than 25 and limit that to atmost 5. how do I generate various subsets for the same ?
# select * from employee where age > 25 ;
will list all the employee whose age is > 25
tupleid = { 1,2,3,4,5,6,7,8,9 }
#select * from employee where age > 25 limit 5 ;
will list any 5 tuples from it.
tupleid on first time execution = {1,2,3,4,5}
But I need some kind of permutation of the 5 set tuple where I get
{1,2,3,4,5} {2,3,4,5,6} .. and so on ..
Is there a way to generate the same in sql / postgres ?
Edit 1:
Since the question dint seem to be clear at first.
I have added a sample table and sql Query I was talking about . sqlfiddle.com/#!2/69a0c/2 If you see the output of the 2nd query I only get to see [1,2,3,4,5] tuples are the answer. I want [1,2,3,4,5] , [1,2,3,4,6] , [1,2,3,4,7] and soo on unique combinations as the answer .
To get your "kind of permutations", use OFFSET:
SELECT *
FROM employee
WHERE age > 25
ORDER BY employee_id -- or whatever
OFFSET 1
LIMIT 5;
To get a random sample:
SELECT *
FROM employee
WHERE age > 25
ORDER BY random()
LIMIT 5;
Postgres. In MySQL you can use RAND() instead.