How to get partial sum of column and finds which rows has partial sum in mysql - mysql

<!-- language: lang-none -->
+----------------------------------------+
| Here is my sample table |
+----------------------------------------+
| Date Person Column_1 Column_2 |
| 15-03-13 A 100 NULL |
| 15-03-13 B NULL 100 |
| 16-03-13 A 100 50 |
| 16-03-13 B NULL NULL |
| 17-03-13 A 100 50 |
| 17-03-13 B 20 30 |
+----------------------------------------+
Now i wanted to do sum(column_1) by date. But i also need to which sum of row includes NULL data.
here is the output result I wanted to achieve
+---------------------------------+
| Date SUM includesNullval |
+---------------------------------+
| 15-03-13 100 true |
| 16-03-13 100 true |
| 17-03-13 120 false |
+---------------------------------+
I don't know to achieve above output. Can anybody give me any idea or solution of this problem?

Note both the form and content of the answer.
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(date DATE NOT NULL
,person CHAR(1) NOT NULL
,column_1 INT NULL
,column_2 INT NULL
,PRIMARY KEY(date,person)
);
INSERT INTO my_table VALUES
('2013-03-15','A', 100, NULL),
('2013-03-15','B',NULL, 100),
('2013-03-16','A', 100, 50),
('2013-03-16','B',NULL, NULL),
('2013-03-17','A', 100, 50),
('2013-03-17','B', 20, 30);
SELECT date, SUM(column_1), MAX(column_2 IS NULL) incnull FROM my_table GROUP BY date;
+------------+---------------+---------+
| date | SUM(column_1) | incnull |
+------------+---------------+---------+
| 2013-03-15 | 100 | 1 |
| 2013-03-16 | 100 | 1 |
| 2013-03-17 | 120 | 0 |
+------------+---------------+---------+

Using a case statement will help you here:
Select Date_Column, sum(column_A),
MAX(CASE when column_A is NULL then 'True' else 'False' end) "Includes_NULL_Value"
from table_x
group by Date_column;
REX TESTER

Related

Sort mysql result by two column, but with a “holed” column

I have the following initial situation:
+------------+-------------+
| legacyRank | forcedRank |
+------------+-------------+
| 0 | NULL |
| 1 | 6 |
| 2 | NULL |
| 3 | 1 |
| 4 | NULL |
| 5 | NULL |
| 6 | 2 |
+------------+-------------+
You could generate this table by the following schema:
CREATE TABLE two_column_order (
legacyRank VARCHAR(45),
forcedRank VARCHAR(45)
);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (5, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (6, 2);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (7, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (0, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (1, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (2, 6);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (3, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (4, 1);
SELECT * FROM two_column_order
order by
CASE when `forcedRank` <> NULL THEN `forcedRank`
ELSE `legacyRank`
END
The goal is to put each line with no-NULL forcedRank column in the accurate position mentioned in this forcedRank column. The expected rendering is like:
+------------+-------------+
| legacyRank | forcedRank |
+------------+-------------+
0 | 0 | NULL |
1 | 3 | 1 |
2 | 6 | 2 |
3 | 2 | NULL |
4 | 4 | NULL |
5 | 5 | NULL |
6 | 6 | 6 |
+------------+-------------+
As you see, each line take the position ordered by the forcedRank column if not NULL. When the the NULL rows still sorted by the legacyRank column in the positions leaved unoccupied by the non-NULL rows, but never shift the forced rows.
In this order, I tried to use the CASE WHEN syntax inside the ORDER BY like this:
SELECT * FROM two_column_order
order by
CASE WHEN (`forcedRank` is NULL ) THEN `legacyRank`
END ,
-`forcedRank` DESC,
`legacyRank`
But the result doesn’t really feat my expectations:
+------------+-------------+
| legacyRank | forcedRank |
+------------+-------------+
| 3 | 1 |
| 6 | 2 |
| 6 | 6 |
| 0 | NULL |
| 2 | NULL |
| 4 | NULL |
| 5 | NULL |
+------------+-------------+
So how can I make the legacyRank column get order beyond the forcedrank rows without shift them?
NULL can't tbe comapred like that you need to use ISor in your case IS NOT
SELECT * FROM two_column_order
order by
CASE when `forcedRank` IS NOT NULL THEN `forcedRank`
ELSE `legacyRank`
END
legacyRank
forcedRank
0
null
1
null
4
1
6
2
3
null
5
null
2
6
7
null
fiddle
As the first answer won't give you the correct answer.
i have changed the order by adding a decimal point to the original number so that it will be bigger than the new forced rank.
it will keep the order and a the forced number is smaller then the legayrank, it get you follwoing result
SELECT * FROM two_column_order
order by
CASE when `forcedRank` IS NOT NULL THEN `forcedRank`
ELSE `legacyRank` + .1
END
legacyRank
forcedRank
0
null
4
1
1
null
6
2
3
null
5
null
2
6
7
null
fiddle

How to extract different json elements from the same table in SQL query?

I am querying from a table with the following format:
id|provider|score
--------------------------------
1 | att | '{"attscore":300}'
1 | verizon | '{"verizonscore":299}'
2 | att | '{"attscore":200}'
3 | verizon | '{"verizonscore":155}'
I am trying to get a table that looks like the following:
id|attscore|verizonscore
-------------------------
1 | 300 | 299
2 | 200 | null
3 | null | 155
Note that used to json in sql
CREATE TABLE table1 (
`id` INTEGER,
`provider` VARCHAR(7),
`score` VARCHAR(22)
);
INSERT INTO table1
(`id`, `provider`, `score`)
VALUES
('1', 'att', '{"attscore":300}'),
('1', 'verizon', '{"verizonscore":299}'),
('2', 'att', '{"attscore":200}'),
('3', 'verizon', '{"verizonscore":155}');
SELECT
id,
GROUP_CONCAT(CASE WHEN provider = 'att' THEN `score`->"$.attscore" ELSe NULL END) attscore
,GROUP_CONCAT(CASE WHEN provider = 'verizon' THEN `score`->"$.verizonscore" ELSe NULL END) verizonscore
FROM table1
GROUP BY id
id | attscore | verizonscore
-: | :------- | :-----------
1 | 300 | 299
2 | 200 | null
3 | null | 155
db<>fiddle here
This works with a fixed number of column quite well, if you have much more of these you need to do something like this

Mysql - Check if VARCHAR column has a missing value on its incrementation

I'm trying to find out if my values inserted are auto-incrementing correctly or if for any reason one has failed to be inserted, deleted or gone "missing". I've tried several answers from Stackoverflow but they were mainly pointing out autoincrementable int values so they did not help since mine is a VARCHAR value that follows the following sequence:
AA000001
AA000002
...
AA000100
...
AA213978
and so on...
Thanks for your time.
You can declare SQL Vars in Query and calculate the difference in each iteration, as shown in the example below:
Schema
create table MyTable
( ai int auto_increment primary key,
id varchar(100) not null
);
insert MyTable (id) values
('AA000001'),
('AA000002'),
('AA000005'),
('AA000008'),
('AA000009'),
('AA000010');
Query
select id
FROM
(
select
t.id,
SUBSTRING(t.id,3) as s,
CAST(SUBSTRING(t.id,3) AS UNSIGNED) - #lastId as diff,
if( #lastId = 0, 0, CAST(SUBSTRING(t.id,3) AS UNSIGNED) - #lastId) as Difference,
#lastId := CAST(SUBSTRING(t.id,3) AS UNSIGNED) as dummy
from
`MyTable` t,
( select #lastId := 0) SQLVars
order by
t.id
) d
WHERE diff>1;
This is the inside query (not the final result set of the above)
+----------+--------+------+------------+-------+
| id | s | diff | Difference | dummy |
+----------+--------+------+------------+-------+
| AA000001 | 000001 | 1 | 0 | 1 |
| AA000002 | 000002 | 1 | 1 | 2 |
| AA000005 | 000005 | 3 | 3 | 5 |
| AA000008 | 000008 | 3 | 3 | 8 |
| AA000009 | 000009 | 1 | 1 | 9 |
| AA000010 | 000010 | 1 | 1 | 10 |
+----------+--------+------+------------+-------+
Actual Results of Above Query:
+----------+
| id |
+----------+
| AA000005 |
| AA000008 |
+----------+
Here's the SQL Fiddle.
To simply test if there are missing values,
select count(*) <> max(right(col, 6))-min(right(col, 6))+1 || count(*) <> count(distinct col)

Update the next row of the target row in MySQL

Suppose I have a table that tracks if a payment is missed like this:
+----+---------+------------+------------+---------+--------+
| id | loan_id | amount_due | due_at | paid_at | missed |
+----+---------+------------+------------+---------+--------+
| 1 | 1 | 100 | 2013-08-17 | NULL | NULL |
| 5 | 1 | 100 | 2013-09-17 | NULL | NULL |
| 7 | 1 | 100 | 2013-10-17 | NULL | NULL |
+----+---------+------------+------------+---------+--------+
And, for example, I ran a query that checks if a payment is missed like this:
UPDATE loan_payments
SET missed = 1
WHERE DATEDIFF(NOW(), due_at) >= 10
AND paid_at IS NULL
Then suppose that the row with id = 1 gets affected. I want the amount_due of row with id = 1 be added to the amount_due of the next row so the table would look like this:
+----+---------+------------+------------+---------+--------+
| id | loan_id | amount_due | due_at | paid_at | missed |
+----+---------+------------+------------+---------+--------+
| 1 | 1 | 100 | 2013-08-17 | NULL | 1 |
| 5 | 1 | 200 | 2013-09-17 | NULL | NULL |
| 7 | 1 | 100 | 2013-10-17 | NULL | NULL |
+----+---------+------------+------------+---------+--------+
Any advice on how to do it?
Thanks
Take a look at this :
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE loan_payments
(`id` int, `loan_id` int, `amount_due` int,
`due_at` varchar(10), `paid_at` varchar(4), `missed` varchar(4))
;
INSERT INTO loan_payments
(`id`, `loan_id`, `amount_due`, `due_at`, `paid_at`, `missed`)
VALUES
(1, 1, 100, '2013-09-17', NULL, NULL),
(3, 2, 100, '2013-09-17', NULL, NULL),
(5, 1, 100, '2013-10-17', NULL, NULL),
(7, 1, 100, '2013-11-17', NULL, NULL)
;
UPDATE loan_payments AS l
LEFT OUTER JOIN (SELECT loan_id, MIN(ID) AS ID
FROM loan_payments
WHERE DATEDIFF(NOW(), due_at) < 0
GROUP BY loan_id) AS l2 ON l.loan_id = l2.loan_id
LEFT OUTER JOIN loan_payments AS l3 ON l2.id = l3.id
SET l.missed = 1, l3.amount_due = l3.amount_due + l.amount_due
WHERE DATEDIFF(NOW(), l.due_at) >= 10
AND l.paid_at IS NULL
;
Query 1:
SELECT *
FROM loan_payments
Results:
| ID | LOAN_ID | AMOUNT_DUE | DUE_AT | PAID_AT | MISSED |
|----|---------|------------|------------|---------|--------|
| 1 | 1 | 100 | 2013-09-17 | (null) | 1 |
| 3 | 2 | 100 | 2013-09-17 | (null) | 1 |
| 5 | 1 | 200 | 2013-10-17 | (null) | (null) |
| 7 | 1 | 100 | 2013-11-17 | (null) | (null) |
Unfortunately I don't have time at the moment to write out full-blown SQL, but here's the psuedocode I think you need to implement:
select all DISTINCT loan_id from table loan_payments
for each loan_id:
set missed = 1 for all outstanding payments for loan_id (as determined by date)
select the sum of all outstanding payments for loan_id
add this sum to the amount_due for the loan's next due date after today
Refer to this for how to loop using pure MySQL: http://dev.mysql.com/doc/refman/5.7/en/cursors.html
I fixed my own problem by adding a missed_at field. I put the current timestamp ($now) in a variable before I update the first row to missed = 1 and missed_at = $now then I ran this query to update the next row's amount_due:
UPDATE loan_payments lp1 JOIN loan_payments lp2 ON lp1.due_at > lp2.due_at
SET lp1.amount_due = lp2.amount_due + lp1.amount_due
WHERE lp2.missed_at = $now AND DATEDIFF(lp1.due_at, lp2.due_at) <= DAYOFMONTH(LAST_DAY(lp1.due_at))
I wish I could use just use LIMIT 1 to that query but it turns out that it's not possible for an UPDATE query with a JOIN.
So all in all, I used two queries to achieve what I want. It did the trick.
Please advise if you have better solutions.
Thanks!

mysql sum with group by for different column values

create table t (
`place_id` int(11) NOT NULL ,
`area_sq` int (11) NOT NULL,
`nxx` int(11) NOT NULL
);
insert into t values(1, 50, 1);
insert into t values(2, 90, 2);
insert into t values(2, 20, 1);
insert into t values(2, 10, 0);
insert into t values(2, 10, 1);
insert into t values(3, 10, 3);
| PLACE_ID | AREA_SQ | NXX |
|----------|---------|-----|
| 1 | 50 | 1 |
| 2 | 90 | 2 |
| 2 | 20 | 1 |
| 2 | 10 | 0 |
| 2 | 10 | 1 |
| 3 | 10 | 3 |
above is my table called t. I need to get the following result
| PLACE_ID | SUM(AREA_SQ) |nxx |
|----------|--------------|----|
| 1 | 50 | 1|
| 2 | 100 | 2|
| 2 | 40 | 1|
| 3 | 10 | 3|
I want the sum of area_sq of same nxx plus sum of area_sq if nxx=0
my current query is like this. But I do not want sum of area_sq if nxx=0 as separate row, instead I want to add sum(area_sq) to others if nxx=0. So want to group by nxx as well but for nxx=0 area_sq should be added to the other nxx values. I am really sorry for incorrect English. I tried different things but none was successful. Thanks in advance.
select place_id,sum(area_sq) from t group by place_id,nxx
You can have an extra subquery to be join which separately calculates the total sum of nxx = 0.
SELECT t.PLACE_ID,
SUM(t.AREA_SQ) + COALESCE(s.AREA_SQ,0) totalSum,
NXX
FROM t LEFT JOIN
(
SELECT PLACE_ID, SUM(AREA_SQ) AREA_SQ
FROM t
WHERE NXX = 0
GROUP BY PLACE_ID
) s ON t.PLACE_ID = s.PLACE_ID
WHERE NXX <> 0
GROUP BY t.PLACE_ID, NXX
ORDER BY t.PLACE_ID, totalSum DESC
SQLFiddle Demo