Use previous row result when current row is null - mysql

I have a table that has daily records of transactions and some rows are missing data which will make plotting a daily graph inconsistent.
I want a query to use the last row result when the current one is null so that it can look something like this:
The structure of my table looks like this:
I have tried working on this query to select the previous row and update the current row if it is null but is not dynamic.
SELECT BALANCE
FROM tbl_batch_balances_null
WHERE id =
(select min(id)
from tbl_batch_balances_null where id < '2' and balance is not null)

Schema (MySQL v5.7)
DROP TABLE IF EXISTS alfie;
CREATE TABLE alfie
(id INT AUTO_INCREMENT PRIMARY KEY,
store CHAR(1) NULL,
product INT NULL
);
INSERT INTO alfie VALUES
( 7,'a',3),
( 8,null,null),
( 9,null,null),
(10,null,null),
(11,'a',1),
(12,'a',1),
(13,'a',1),
(14,null,null),
(15,null,null),
(16,'b',2),
(17,null,null),
(18,null,null),
(19,null,null);
Query #1
SELECT a.id,
COALESCE(a.store, c.store) store,
COALESCE(a.product,c.product) product
FROM alfie a
LEFT
JOIN
( SELECT x.*,
MAX(y.id) y_id
FROM alfie x
JOIN alfie y
ON y.id < x.id
AND y.store IS NOT NULL
WHERE x.store IS NULL
GROUP
BY x.id
) b
ON b.id = a.id
LEFT
JOIN alfie c
ON c.id = b.y_id
ORDER
BY id;
id
store
product
7
a
3
8
a
3
9
a
3
10
a
3
11
a
1
12
a
1
13
a
1
14
a
1
15
a
1
16
b
2
17
b
2
18
b
2
19
b
2
View on DB Fiddle

Related

Aggregate rows by id comparing column values

I have the following table that groups users by their permissions
userIds permissions
4,5,7,8 100,1600,500,501,502,400,401,1500,1501
The numbers in the permissions column are the sections ids.
Some of these sections may have other data associated which I retrieved and stored in another table.
sectionId userId resourceId
100 4 NULL
1600 4 NULL
500 4 NULL
501 4 NULL
502 4 NULL
400 4 NULL
401 4 1
1500 4 NULL
1501 4 NULL
100 5 NULL
1600 5 NULL
500 5 NULL
501 5 NULL
502 5 NULL
400 5 NULL
401 5 1,2
1500 5 NULL
1501 5 NULL
100 7 NULL
1600 7 NULL
500 7 NULL
501 7 NULL
502 7 NULL
400 7 NULL
401 7 2
1500 7 NULL
1501 7 NULL
100 8 NULL
1600 8 NULL
500 8 NULL
501 8 NULL
502 8 NULL
400 8 NULL
401 8 1
1500 8 NULL
1501 8 NULL
My goal is to compare, for each user in the userIds column of the first table (splitted by comma), every row of the second table in order to check if each user has the same resourceId value for that specific sectionId.
If one or more users have the same resourceId value for each section I want to keep them group together, otherwise they need to be on different rows.
This is the output I'm expecting from the sample data provided:
userIds permissions
4,8 100,1600,500,501,502,400,401,1500,1501
5 100,1600,500,501,502,400,401,1500,1501
7 100,1600,500,501,502,400,401,1500,1501
UPDATE
I managed to get the desidered output in the following way:
-- Numbers table creation
DROP temporary TABLE IF EXISTS tally;
CREATE temporary TABLE tally
(
n INT NOT NULL auto_increment PRIMARY KEY
);
INSERT INTO tally
(n)
SELECT NULL
FROM (SELECT 0 AS N
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9) a,
(SELECT 0 AS N
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9) b;
-- Split users by comma from first table
DROP temporary TABLE IF EXISTS tmppermissions2;
CREATE temporary TABLE tmppermissions2
(
userid VARCHAR(255) NOT NULL,
permissions TEXT NOT NULL
);
INSERT INTO tmppermissions2
SELECT userid,
permissions
FROM (SELECT Substring_index(Substring_index(t.userids, ',', tally.n), ',', -1
)
userId,
t.permissions
permissions
FROM tally
INNER JOIN tmppermissions t
ON Char_length(t.userids) - Char_length(
REPLACE(t.userids, ',',
'')) >=
tally.n - 1
ORDER BY n) AS split;
-- Gets the users with the same permissions
DROP temporary TABLE IF EXISTS sharedprofiles;
CREATE temporary TABLE sharedprofiles
(
userids VARCHAR(255) NOT NULL,
permissions TEXT NOT NULL,
profileid INT(11)
);
INSERT INTO sharedprofiles
SELECT Group_concat(userid),
permissions,
NULL
FROM tmppermissions2
WHERE userid NOT IN (SELECT split.userid
FROM (SELECT Substring_index(Substring_index(r.userids,
',',
t.n), ',', -1)
userId
FROM tally t
INNER JOIN tmppermissions r
ON Char_length(r.userids)
- Char_length(
REPLACE(r.userids, ',',
'')) >=
t.n - 1
WHERE Position(',' IN r.userids) > 0
ORDER BY n) AS split
WHERE split.userid IN (SELECT *
FROM (SELECT Group_concat(userid
ORDER
BY userid ASC)
AS
users
FROM
tmpcurrentresources2
GROUP BY resourceid,
sectionid
ORDER BY users) b
WHERE Position(',' IN b.users) =
0))
GROUP BY permissions
ORDER BY Group_concat(userid);
-- Gets the users with specific permissions
DROP temporary TABLE IF EXISTS singleprofiles;
CREATE temporary TABLE singleprofiles
(
userid VARCHAR(255) NOT NULL,
permissions TEXT NOT NULL,
profileid INT(11)
);
INSERT INTO singleprofiles
SELECT userid,
permissions,
NULL
FROM tmppermissions2
WHERE userid IN (SELECT split.userid
FROM (SELECT Substring_index(Substring_index(r.userids, ',',
t.n),
',', -1)
userId
FROM tally t
INNER JOIN tmppermissions r
ON Char_length(r.userids) -
Char_length(
REPLACE(r.userids, ',',
'')) >=
t.n - 1
WHERE Position(',' IN r.userids) > 0
ORDER BY n) AS split
WHERE split.userid IN (SELECT *
FROM (SELECT Group_concat(userid
ORDER BY
userid ASC)
AS
users
FROM tmpcurrentresources2
GROUP BY resourceid,
sectionid
ORDER BY users) b
WHERE Position(',' IN b.users) = 0))
ORDER BY userid;
-- Merge the results
SELECT *
FROM sharedprofiles
UNION
SELECT *
FROM singleprofiles;
I'm wondering if there is a more concise way to accomplish the same result.
The solution (as I suspect you already know) is to normalise your schema.
So instead of...
userIds permissions
4,5 100,1600,500
...you might have
userIds permissions
4 100
4 1600
4 500
5 100
5 1600
5 500

How to retrieve complete data from two tables based on user_name along with max date in Hive

For example I have a table A and B with the following data:
A:
user_name date1 count1 count2
X 15 1 1
X 30 1 3
Y 04 1 3
B:
user_name date1 count3 count4 status
X 15 11 1 Y
X 30 13 3 N
Y 04 16 3 NA
How to join these 2 tables for each feedname with max date.
I need the output like these:
username date1 count1 count4 status
X 30 1 3 N
like these way.
Can anyone plz help in these situation.
Since according to your comment every combination (user_name, date1) exists in both tables, you can use e.g.
select a.*, b.count3, b.count4, b.status
from tableA as a
join tableB as b
on a.user_name = b.user_name and
a.date1 = b.date1
where not exists
(select 1 from tableA as a1
where a1.user_name = a.user_name
and a1.date1 > a.date1);
You want to have an index on (user_name, date1) to speed it up.
As a side note: If every entry in tableA has exactly 1 entry in tableB and vice-versa (it's not clear from your description if that is the case, but it looks like it), and thus (user_name, date1) would be a primary key in both tables, you absolutely should add the columns count3, count4 and status to tableA and get rid of tableB. You can still use the above code (without join) to find only the max entry per user.

MySQL - Select only last record of each foreign key

I have 3 tables that go like this (stripped version):
Create table A (
AID Int NOT NULL,
PRIMARY KEY (AID)
)
Create table B (
BID Int NOT NULL,
AID Int NOT NULL,
PRIMARY KEY (BID),
FOREIGN KEY (AID) REFERENCES A(AID)
)
Create table C (
CID Int NOT NULL,
BID Int NOT NULL,
Price Decimal(12,4) NOT NULL,
PRIMARY KEY (CID),
FOREIGN KEY (BID) REFERENCES B(BID)
)
Each record in A can only have one up-to-date record in B (last ID is considered as latest) for example: we have a record in A that has 3 associated records in B, but only the last record is considered to be up-to-date and only that one should be used.
Each time a record in B is updated (in this case inserted), all records in C are duplicated and point to the newly created record in B.
How could I get the SUM of C.Price of all A records.
So far I got: (but distinct doesn't seem to work)
SELECT SUM(Price)
FROM C
INNER JOIN (
SELECT DISTINCT t2.AID, t2.BID
FROM (
SELECT BID, AID
FROM B
INNER JOIN (
SELECT AID
FROM A
-- some other statements go here
) t1
ON t1.AID = AID
ORDER BY BID DESC
) t2
) t3
ON t3.BID = C.BID
I hope I explained well enough. :/
The following should do what you want:
select b.aid, sum(c.price)
from b join
c
on c.bid = b.bid
where b.bid = (select max(b2.bid) from b b2 where b2.aid = b.aid)
group by b.aid;
The join brings the tables together. Note that a is not necessary -- unless you want rows from a that have no corresponding row in b (in which case, a would go first with a left join).
The where clause chooses the most recent b record for each aid. The group by is used to get the sum.
Each time a record in B is updated (in this case inserted), all
records in C are duplicated and point to the newly created record in
B.
This means that there is only record in C having the latest BID value. Which means, you don't need to SUM them, you only need to fetch the latest record.
Just to illustrate with sample data :
Table A :
AID
1
2
3
Table B :
BID AID
1 1
2 1
3 1
4 2
5 2
6 2
7 3
8 3
9 3
Table C :
CID BID Price
1 1 12
2 2 12
3 3 20
4 4 2
5 5 1
6 6 12
7 7 22
8 8 21
9 9 23
You can use this :
SELECT ab.AID,
c.Price
FROM (SELECT a.aid, MAX(b.BID) AS maxB
FROM a a
INNER JOIN b b ON a.AID = b.AID
GROUP BY a.AID
) ab
INNER JOIN C c ON ab.maxB = c.BID
Observe that there is no sum here.
This will give you the result :
AID PRICE
1 20
2 12
3 23
You can see this here -> http://sqlfiddle.com/#!9/4943cd/3
Gordon's answer would also give you the same result -> http://sqlfiddle.com/#!9/4943cd/2
You can see that irrespective of whether SUM is used or not, the result is the same.
NOTE : However, Gordon's answer (which is the marked answer here) is incorrect since it doesn't take into account the fact that if any values are removed from table A, then your result set would have redundant values.
You can observe this if you slightly change the original data set as below :
Table A :
AID
1
Table B :
BID AID
1 1
2 1
3 1
4 2
5 2
6 2
7 3
8 3
9 3
Table C :
CID BID Price
1 1 12
2 2 12
3 3 20
4 4 2
5 5 1
6 6 12
7 7 22
8 8 21
9 9 23
Gordon's query would yield you the result :
AID PRICE
1 20
2 12
3 23
which is incorrect as there are no AID values 2 and 3 anymore.
You need to join on table A to fetch currently existing values of AID in table A.
Hope this helps!!!

including limit to select top n rows within a group by clause

I have the following 2 tables:
create table1
(
SENDER int,
RECEIVER int,
TIME time,
TYPE char(1)
);
create table2
(
ID int,
Y int,
CONTACT int,
DATE time
);
I am executing the following join query:
SELECT B.ID, A.RECEIVER AS Z, A.SENDER AS CONTACT, A.TYPE, A.TIME
FROM table1 A
JOIN table2 B ON A.RECEIVER = B.CONTACT
WHERE A.TYPE = 'A'
AND A.TIME < B.DATE
How do I modify the query to return only the top 40 results for each (ID,CONTACT) pair using GROUP BY?
I can order the data using the field table2.DATE
since i wanted top 40 results for each ID, i made ID,autoId as a primary key, here autoId is an autoincrement key. so after executing the following query:
SELECT B.ID, A.RECEIVER AS Z, A.SENDER AS CONTACT, A.TYPE, A.TIME
FROM table1 A
JOIN table2 B ON A.RECEIVER = B.CONTACT
WHERE A.TYPE = 'A'
AND A.TIME < B.DATE
i get results such that, the autoId initializes to 1 for each ID
for eg:
ID CONTACT autoId
1 2 1
1 3 2
1 11 3
1 34 4
2 5 1
2 33 2
2 56 3
since autoId is autoincrement, there is already an index on it. after this table is created, i can easily delete the results where autoId is greater than 40. and this while process runs really fast!

UPDATE with INNER JOIN or MIN?

I am trying to transfer some data between tables. The 'NEW' table can have multiple entries of the data that was originally not meant to have multiple entries in the 'OLD' table. I would like to take the data from the 'OLD' table and copy it over to the new table where the NEW.ID is the lowest where new.OtherID=old.OtherID, basically a MIN(ID) per group of OtherID's equal to each other.
'NEW' table
ID | OtherID | Data
1 1 NULL
2 1 NULL
3 2 NULL
4 3 NULL
5 3 NULL
'OLD'
OtherID | Data <br>
1 data1
2 data2
3 data3
4 data4
5 data5
Desired Outcome on updated 'NEW' table:
ID | OtherID | Data <br>
1 1 data1
2 1 NULL
3 2 data2
4 3 data3
5 3 NULL
etc
Thanks!
This is how you could use INNER JOIN with UPDATE in MySQL:
UPDATE NEW n
INNER JOIN (
SELECT
OtherID,
MIN(ID) AS ID
FROM NEW
GROUP BY OtherID
) m ON n.ID = m.ID
INNER JOIN OLD o ON n.OtherID = o.OtherID
SET n.Data = o.Data
You can try:
UPDATE new
SET Data = ( SELECT DATA FROM old WHERE otherID = new.otherID )
WHERE NOT EXIST
( SELECT NULL FROM new AS new2
WHERE new2.id < new.id
AND new2.otherID = new.otherID )
Note that this is standard SQL92 and should work with any RDBMS.
This worked for me in PostgreSQL, though I may have gotten the quoting wrong for MySQL.
UPDATE newtable SET
`Data` = oldtable.`Data`
FROM
oldtable
WHERE
newtable.`ID` IN (
SELECT MIN(sub_newtable.`ID`)
FROM newtable sub_newtable
GROUP BY
sub_newtable.`OtherID`
)
AND newtable.`OtherID` = oldtable.`OtherID`
You can use:
UPDATE `NEW`
LEFT JOIN `OLD`
ON `NEW`.`OtherID` = `OLD`.`ID`
SET `NEW`.`Data` = `OLD`.`Data`
EDIT: I'm sorry, this will update all records that correspond to columns in OLD.