Get First Non-null Value From Selected Rows For Every Column - mysql

I am trying to create a query that can get First Non-null value for selected columns from the table.
I cant fire multiple queries and union it per every column as I have so many columns. I tried to create a query using answers form some SO questions. but it doesn't work for me.
Example Table
| orders| id | default_address |
|-------|------|-----------------|
| 1 | 1 | null |
| 2 | null | null |
| 3 | 2 | 3 |
Expected Result
| id | default_address |
|----|-----------------|
| 1 | 3 |
Another Example Table
| orders| id | default_address |
|-------|------|-----------------|
| 1 | 1 | null |
| 2 | null | 5 |
| 3 | 2 | 3 |
Expected Result
| id | default_address |
|----|-----------------|
| 1 | 5 |
What i tried is here
http://sqlfiddle.com/#!9/84a5c/1
http://sqlfiddle.com/#!9/574481/2

Here is one way to do this using analytic functions, assuming you are using MySQL 8+:
WITH cte AS (
SELECT *,
COUNT(id) OVER (ORDER BY orders) cnt_id,
COUNT(default_address) OVER (ORDER BY orders) cnt_addr
FROM yourTable
)
SELECT
MAX(CASE WHEN cnt_id = 1 THEN id END) AS id,
MAX(CASE WHEN cnt_addr = 1 THEN default_address END) AS default_address
FROM cte;
This assumes that there actually exist a third column orders which generates the ordering shown in your sample data.
This trick works because the COUNT() function by default only counts non NULL values. So, used a window function with the ordering given by the orders column, it would only equal 1 at the first non NULL value.
For earlier MySQL versions:
SELECT
MAX(id) AS id,
MAX(default_address) AS default_address
FROM
(
SELECT
CASE WHEN (SELECT COUNT(t2.id) FROM yourTable t2
WHERE t2.orders <= t1.orders) = 1 THEN id END AS id,
CASE WHEN (SELECT COUNT(t2.default_address) FROM yourTable t2
WHERE t2.orders <= t1.orders) = 1 THEN default_address END AS default_address
FROM yourTable t1
) t;

Related

get empty instead of repeated value in query

I have a table like this
|num|id|name|prj|
| 1 | 1|abc | 1 |
| 2 | 1|efg | 1 |
| 3 | 1|cde | 1 |
| 4 | 2|zzz | 1 |
I want to run a query like this:
SELECT * FROM table WHERE prj=1 ORDER BY name
but printing out repeated values only once. I want to keep all the rows and I would like to do this at database level and not on the presentation layer (I know how to do it in php).
Desired result is
|num|id|name|prj|
| 1 | 1|abc | 1 |
| 3 | |cde | 1 |
| 2 | |efg | 1 |
| 4 | 2|zzz | 1 |
any hint on where to start from to build that query?
Use a session variable to test if the previous ID is the same as the current ID:
SELECT num, IF(#lastid = id, '', #lastid := id) AS id, name, prj
FROM table
CROSS JOIN (SELECT #lastid := null) x
ORDER BY table.id, name
DEMO
Note that you need to qualify table.id, because ORDER BY defaults to using the alias from the SELECT list if it's the same as a table column, and that would order the empty fields first.

MySql: update row that requires some logic processing

Given the following table:
id | company | names | group
-------------------------------------
0 | 1 | John | 1
1 | 1 | Doe | null //populate with preceding group number (i.e. 1)
2 | 1 | Yo | null //populate with preceding group number (i.e. 1)
3 | 1 | Zoe | null //populate with preceding group number (i.e. 1)
4 | 1 | Jack | 2
5 | 1 | Doe | null //populate with preceding group number (i.e. 2)
6 | 1 | Yo | null //populate with preceding group number (i.e. 2)
May I know how i can update only the null values to its preceding group number via sql statements? Thanks.
Try this:
UPDATE tablename t1
JOIN (
SELECT ID, #s:=IF(`group` IS NULL, #s, `group`) `group`
FROM (SELECT * FROM tablename ORDER BY ID) r,
(SELECT #s:=NULL) t
) t2
ON t1.ID = t2.ID
SET t1.`group`= t2.`group`
SQLFIDDLE DEMO

I need to get the average for every 3 records in one table and update column in separate table

Table Mytable1
Id | Actual
1 ! 10020
2 | 12203
3 | 12312
4 | 12453
5 | 13211
6 | 12838
7 | 10l29
Using the following syntax:
SELECT AVG(Actual), CEIL((#rank:=#rank+1)/3) AS rank FROM mytable1 Group BY rank;
Produces the following type of result:
| AVG(Actual) | rank |
+-------------+------+
| 12835.5455 | 1 |
| 12523.1818 | 2 |
| 12343.3636 | 3 |
I would like to take AVG(Actual) column and UPDATE a second existing table Mytable2
Id | Predict |
1 | 11133
2 | 12312
3 | 13221
I would like to get the following where the Actual value matches the ID as RANK
Id | Predict | Actual
1 | 11133 | 12835.5455
2 | 12312 | 12523.1818
3 | 13221 | 12343.3636
IMPORTANT REQUIREMENT
I need to set an offset much like the following syntax:
SELECT #rank := #rank + 1 AS Id , Mytable2.Actual FROM Mytable LIMIT 3 OFFSET 4);
PLEASE NOTE THE AVERAGE NUMBER ARE MADE UP IN EXAMPLES
you can join your existing query in the UPDATE statement
UPDATE Table2 T2
JOIN (
SELECT AVG(Actual) as AverageValue,
CEIL((#rank:=#rank+1)/3) AS rank
FROM Table1, (select #rank:=0) t
Group BY rank )T1
on T2.id = T1.rank
SET Actual = T1.AverageValue

How to query last records

so, if i have this table:
| ID | Date | Status | Value |
| 1 | 2-2-2012 | A | 5 |
| 2 | 3-4-2012 | B | 3 |
| 1 | 5-6-2012 | C | 1 |
| 2 | 1-1-2012 | D | 4 |
and I need to get total value and "most recent" status for every IDs, how to do the query? i tried using group by , but the somehow only oldest status shown in the query result.
I need to get the data became like this:
| ID | Date | Status |sum(Value)|
| 2 | 3-4-2012 | B | 7 |
| 1 | 5-6-2012 | C | 6 |
i'm a total newbie in this SQL thing, not an IT person, just because my boss ask to extract some data from our database....
thanks in advance...
Since you have not mentioned any RDBMs, the query below works on almost all RDBMS.
This uses a subquery which separately gets the latest date (assuming that the data type of date is really stored as DATE and not as a string) for every ID. The result of the subquery is then joined back on the table itself in order to get the other columns.
SELECT a.ID, a.Date, a.Status, b.TotalSum
FROM tableName a
INNER JOIN
(
SELECT ID, MAX(date) max_date, SUM(Value) totalSum
FROM tableName
GROUP BY ID
) b ON a.ID = b.ID AND
a.date = b.max_date
SQLFiddle Demo
If you are using mysql then this will work
SELECT id,date,status,sum(value)
FROM (select * from yourTable order by DATE desc ) t
group by ID
order by ID desc

MySql sort ascendingly conditionally

Trying to sort rows from lowest to highest continually, or rather repeatedly using MySql. For example: if a column has the following values: 1,3,2,4,2,1,4,3,5, then it should end up like this 1,2,3,4,5,1,2,3,4. So it goes from lowest to highest, but tries to sort again from lowest to highest multiple times.
For large sets, the semi-JOIN operation (the approach in the answer from Strawberry) may create an unwieldy resultset. (Then again, MySQL may have some optimizations in there.)
Another alternative available in MySQL is to use "user variables", like this:
SELECT r.mycol
FROM ( SELECT IF(q.mycol=#prev,#seq := #seq + 1,#seq := 1) AS seq
, #prev := q.mycol AS mycol
FROM mytable q
JOIN (SELECT #prev := NULL, #seq := NULL) p
ORDER BY q.mycol
) r
ORDER BY r.seq, r.mycol
Let me unpack that a bit, and explain what it's doing, starting with the inner query (inline view aliased as r.) We're telling MySQL to get the column (mycol) containing the values you want to sort, e.g. 1,3,2,4,2,1,4,3,5 and we're telling MySQL to order these in ascending sequence: 1,1,2,2,3,3,4,4,5.
The "trick" now is to use a MySQL user variable, so that we can compare the mycol value from the current row to the mycol value from the previous row, and we use that to assign an ascending sequence value, from 1..n on each distinct value.
With that resultset, we can tell MySQL to order by that assigned sequence value first, and then by the value from mycol.
If there is a unique id on each row, then a correlated subquery can be used to get an equivalent result (although this approach is very unlikely to perform as well on large sets)
SELECT r.mycol
FROM mytable r
ORDER
BY ( SELECT COUNT(1)
FROM mytable q
WHERE q.mycol = r.mycol
AND q.id <= r.id
)
, r.mycol
Here's the setup for the test case:
CREATE TABLE mytable (id INT, mycol INT);
INSERT INTO mytable (id, mycol) VALUES
(1,1),(2,3),(3,2),(4,4),(5,2),(6,1),(7,4),(8,3),(9,5);
there is no order two time just this
ORDER BY column ASC
Let's pretend that the PK is a unique integer. Consider the following...
CREATE TABLE seq(id INT NOT NULL PRIMARY KEY,val INT);
INSERT INTO seq VALUES (8,1),(4,2),(1,3),(2,4),(7,0),(6,1),(3,2),(5,5);
SELECT * FROM seq ORDER BY val;
+----+------+
| id | val |
+----+------+
| 7 | 0 |
| 6 | 1 |
| 8 | 1 |
| 3 | 2 |
| 4 | 2 |
| 1 | 3 |
| 2 | 4 |
| 5 | 5 |
+----+------+
SELECT x.*
, COUNT(*) rank
FROM seq x
JOIN seq y
ON y.val = x.val
AND y.id <= x.id
GROUP
BY id
ORDER
BY rank
, val;
+----+------+------+
| id | val | rank |
+----+------+------+
| 7 | 0 | 1 |
| 6 | 1 | 1 |
| 3 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 4 | 1 |
| 5 | 5 | 1 |
| 8 | 1 | 2 |
| 4 | 2 | 2 |
+----+------+------+