Skip 0 and NULL in SQL - php

My previous problem and solution:
Get max and min from fields
This working OK, but i would like skip 0 and NULL in this examples.
For example:
First:
id | title
1 | aaa
2 | bbb
3 | ccc
Second:
id | first_id | one | two | three | four
1 | 1 | 3 | 0 | 4 | 6
2 | 2 | 4 | 4 | 1 | 2
3 | 3 | 1 | NULL | 3 | 4
this should show me:
id | title | min | max
1 | aaa | 3 | 6
2 | bbb | 1 | 4
3 | ccc | 1 | 4
and not:
id | title | min | max
1 | aaa | 0 | 6
2 | bbb | 1 | 4
3 | ccc | 0 | 4
In which example from my previous question is the best way to implement skip 0 and NULL?

Pop these into your clause
SELECT
f.id,
f.title
MIN(LEAST(greatest(coalesce(s.one,0),1), greatest(coalesce(s.two,0),1), greatest(coalesce(s.three,0),1), greatest(coalesce(s.four,0),1))) as min,
MAX(GREATEST(greatest(coalesce(s.one,0),1), greatest(coalesce(s.two,0),1), greatest(coalesce(s.three,0),1), greatest(coalesce(s.four,0),1))) as max
FROM
First f
INNER JOIN Second s
on f.id = s.first_id
GROUP BY
f.id,
f.title
You can use coalesce(fieldName, 1) to turn a null into a 1.
Again, as said in your previous question, this is HORRIBLE use of a query to force an answer. You should be changing the layout of the database.
Edit: I have nutted out the data you want, but before you look at it, be aware that if one of my colleagues wrote a script like this, he would be sacked on the spot. This is HIDEOUS and should NOT BE USED.
select
f.id,
f.title,
(select min(z.myVal) from
(
select
b.id,
b.first_id,
b.one as myVal
from
second b
where
b.one is not null
and b.one > 0
union
select
b.id,
b.first_id,
b.two as myVal
from
second b
where
b.two is not null
and b.two > 0
union
select
b.id,
b.first_id,
b.three as myVal
from
second b
where
b.three is not null
and b.three > 0
union
select
b.id,
b.first_id,
b.four as myVal
from
second b
where
b.four is not null
and b.four > 0
) z
where
f.id=z.first_id) as miniVal,
greatest(
coalesce(s.one,0),
coalesce(s.two,0),
coalesce(s.three,0),
coalesce(s.four,0)
) as maxiVal
from
first f,
second s
where
f.id=s.first_id
output Data
+------+-------+---------+---------+
| id | title | miniVal | maxiVal |
+------+-------+---------+---------+
| 1 | aaaa | 3 | 6 |
| 2 | bbbb | 1 | 4 |
| 3 | cccc | 1 | 4 |
+------+-------+---------+---------+
3 rows in set (0.00 sec)
Running this query made me throw up a little in my mouth. That's how wrong it is to write SQL like this.

While seemingly clunky, this solution should work:
SELECT
a.id, a.title, MIN(b.num) AS min, MAX(b.num) AS max
FROM
first a
LEFT JOIN
(
SELECT first_id, one AS num FROM second UNION ALL
SELECT first_id, two FROM second UNION ALL
SELECT first_id, three FROM second UNION ALL
SELECT first_id, four FROM second
) b ON
a.id = b.first_id AND
b.num IS NOT NULL AND
b.num > 0
GROUP BY
a.id, a.title
What this does is it actually gets each number column into its own row, but only the numbers that are not null and > 0. Before the GROUP BY, the result of the LEFT JOIN would look something like:
id | title | num
---------------------
1 | aaa | 3
1 | aaa | 4
1 | aaa | 6
2 | bbb | 1
2 | bbb | 2
2 | bbb | 4
2 | bbb | 4
3 | ccc | 1
3 | ccc | 3
3 | ccc | 4
Then by the groupings of each first (GROUP BY a.id, a.title), we can use the MIN() and MAX() aggregate functions on the num column to extract minimum and maximum values per first group:
id | title | min | max
----------------------------
1 | aaa | 3 | 6
2 | bbb | 1 | 4
3 | ccc | 1 | 4
In the case that a first_id had all four columns having NULL's or 0's, the min and max values would show up as NULL due to using a LEFT JOIN instead of an INNER JOIN as I believe this is would be a better behavior for your situation:
id | title | min | max
----------------------------
4 | ddd | NULL | NULL

USE:
WHERE COLUMN IS NOT NULL AND COLUMN <> 0;

I think you just need to nest the LEAST expressions:
LEAST(
NULLIF(one,0),
LEAST(
NULLIF(two,0),
LEAST(
NULLIF(three,0),
LEAST(
NULLIF(four,0),
null ))))
Edit I just looked it up. The LEAST function takes multiple arguments:
LEAST( NULLIF(one,0), NULLIF(two,0), NULLIF(three,0), NULLIF(four,0))
Edit 2 I see you want both min and max. Obviously you'd just change LEAST to GREATEST or MIN to MAX as needed.
This may be more straightforward or you may not have a handy least function.
SELECT
f.id, f.title,
(
SELECT MIN(NULLIF(val, 0))
FROM
(
SELECT one AS val UNION ALL
SELECT two UNION ALL
SELECT three UNION ALL
SELECT four
) AS vals
)
) as minval
FROM First f INNER JOIN Second s on f.id = s.first_id
You haven't specified if it's possible for all four columns to be null/0. We may need to tweak for that case.

You can use IFNULL():
WHERE IFNULL(fieldname, 0) != 0

Related

Is there a way to get unique rows for same value

Hello and sorry if this questions isn't good formatted, here's my problem:
for simplicity let's say i have a table with products
-----------------------
| id| age |price|name|
----------------------|
| 0 | 0 | 50 | x |
| 1 | 1 | 51 | x |
| 2 | 2 | 52 | x |
| 3 | 3 | 53 | x |
| 4 | 4 | 54 | x |
| 5 | 5 | 55 | x |
| 6 | 6 | 56 | x |
| 7 | 7 | 57 | x |
| 8 | 8 | 58 | x |
-----------------------
I want to get the price for all products of age 0 and 1 with
select price from products where name='x' and(age=0 or age=1)
and it works returning two rows but when the age is the same logically it returns one row and that's my problem how to get it to return again all the rows i want or if something other is wrong with my logic, thank you in advance
One way to achieve this is to build a derived table of the ages that you want to query against and then JOIN that to the products table. Note that you need to use UNION ALL in the derived table to maintain duplicates. Additional values can be searched by adding another UNION ALL SELECT n to the derived table for each value:
SELECT p.price
FROM (SELECT 0 AS age
UNION ALL
SELECT 0) ages
JOIN products p ON p.age = ages.age
WHERE p.name = 'x'
Output:
price
50
50
Demo on dbfiddle

Select all from table even null and join

I want to SELECT the all rows from meeting table even its null. My table structure:
attandance table:
id | meeting_id | person_id | time
1 | 1 | 12 | 02:02
2 | 1 | 15 | 02:05
3 | 1 | 22 | 02:05
4 | 2 | 1 | 02:20
5 | 2 | 12 | 02:01
6 | 3 | 12 | 02:03
and meeting table:
id | date
1 | 15-12-2014
2 | 17-12-2014
3 | 19-12-2014
4 | 21-12-2014
The output should be:
If I SELECT the person_id 12 then it should return:
date |time
15-12-2014 | 02:02
17-12-2014 | 02:01
19-12-2014 | 02:03
21-12-2014 | NULL
If I SELECT the person_id 1 then it should return:
date |time
15-12-2014 | NULL
17-12-2014 | 02:20
19-12-2014 | NULL
21-12-2014 | NULL
It is a pretty straightforward outer join between the tables:
select
m.id,
m.date
a.time,
c.someColumn
from
meetings m
left outer join attendance a
on m.id=a.meeting_id
and a.person_id=:person
left outer join someTable c
on m.id=c.id
I have written a more detailed answer on these sorts of joins in the question and answer: How can an SQL query return data from multiple tables
Edit: As per the comment by Andrew, the clause for the personID is in the join rather than in a normal where clause because it is an outer join. If the condition was put into the where clause as normal, it would in fact negate the outer join completely.

Randomly select fields where few condition met

Here is the table-
+----+---------+--------+
| id | letters | status |
+----+---------+--------+
| 1 | A | 0 |
| 2 | B | 1 |
| 3 | C | 0 |
| 4 | D | 0 |
| 5 | E | 1 |
| 6 | F | 1 |
| 7 | G | 0 |
| 8 | H | 0 |
+----+---------+--------+
Its need to finds records with the conditions below-
select letters with LIMIT 3
ORDER is RAND()
status true or false both need to present but at-least a presents of
letters with status TRUE but not more then 2
Desire results could be as-
+---------+--------+
| letters | status |
+---------+--------+
| B | 1 |
| E | 1 |
| H | 0 |
+---------+--------+
+---------+--------+
| letters | status |
+---------+--------+
| C | 0 |
| E | 1 |
| H | 0 |
+---------+--------+
But not as-
+---------+--------+
| letters | status |
+---------+--------+
| C | 0 |
| G | 0 |
| H | 0 |
+---------+--------+
+---------+--------+
| letters | status |
+---------+--------+
| B | 1 |
| E | 1 |
| F | 1 |
+---------+--------+
Anyone please help.
Here is a solution in Postgres as requested in the comments.
Assuming status to be NOT NULL.
Assuming at least one row with status FALSE and one with status TRUE exist at all times.
WITH cte AS (
(
SELECT id, letters, status
FROM tbl
WHERE status -- 1 row with status true
ORDER BY random()
LIMIT 1
)
UNION ALL
(
SELECT id, letters, status
FROM tbl
WHERE NOT status -- 1 row with status false
ORDER BY random()
LIMIT 1
)
)
SELECT * FROM cte
UNION ALL -- add another random row
(
SELECT id, letters, status
FROM tbl
LEFT JOIN cte c USING (id)
WHERE c.id IS NULL -- don't select row twice
ORDER BY random()
LIMIT 1
)
ORDER BY random(); -- order 3 rows randomly
MySQL does not supports CTEs.
All parentheses are necessary. Details:
PostgreSQL combine multiple select statements
This is not very efficient for big tables. For better performance consider this related answer:
Best way to select random rows PostgreSQL
If status TRUE and FALSE are not extremely unbalanced, I would write a plpgsql function that loops through the randomly sorted table (or selection like in the the linked answer) until I have three rows with at least one of each status. Would be much faster.

How to select distinct values from multiple columns using mysql php

here is my table:
-------------------------
A1 | A2 | count |
-------------------------
a | b | 1 |
b | a | 1 |
c | a | 1 |
d | b | 1 |
b | d | 1 |
-------------------------
i want to select distinct values from this table with distinct count values.If i use to select A2 column where clause i specified using "b" that time i want result like this
A | count
--------------
a | 1
d | 1
I guess this is what you need ,
select distinct(A2) from table1 where count=1
so that you get this output ,
A | count
--------------
a | 1
d | 1
Are you trying to do this:
select A1 as A,count FROM Table1 WHERE A2 = 'b' GROUP BY A1,count
It will give you the result:
A count
a 1
d 1
See SQL Fiddle

Parent / Child in same table

I have table structure like below:
id |parent|name |value
1 | 0 | aaa |
2 | 0 | bbb |
3 | 0 | ccc |
4 | 1 | | 111
5 | 1 | | 222
6 | 3 | | 333
I want to display parent if it has child records.
Like:
(parent id + name + value first child)
1 - aaa - 111
3 - ccc - 333
There is no meaning of the first child in the database, you can get the first child by the mininum of the id or the minimum of the value, but the values are not stored with a specific order in the table, so you can't tell which value is the first one.
But, assuming that the id is auto incremental column, then value of the first child is the value of the minimum id, then you can do this:
SELECT
t1.parent,
t2.name,
t1.value
FROM tablename AS t1
INNER JOIN
(
SELECT MIN(id) AS id, parent
FROM tablename
GROUP BY parent
) AS t22 ON t22.id = t1.id AND t1.parent = t22.parent
INNER JOIN tablename AS t2 ON t1.parent = t2.id;
See it in action here:
SQL Fiddle Demo
This will give you :
| PARENT | NAME | VALUE |
-------------------------
| 1 | aaa | 111 |
| 3 | ccc | 333 |
Or: You can get it by the minimum value:
SELECT
t1.parent,
t2.name,
MIN(t1.value) AS value
FROM tablename AS t1
INNER JOIN tablename AS t2 ON t1.parent = t2.id
GROUP BY t1.parent, t2.name;
See it in action:
SQL Fiddle Demo
This will give you:
| PARENT | NAME | VALUE |
-------------------------
| 1 | aaa | 111 |
| 3 | ccc | 333 |

Categories