Deleting max value of different names in sql table - mysql

I have a table as follows:
Name Value Date
A 10 2016
A 20 2017
A 30 Null
B 40 2015
B 50 2014
B 60 Null
C 70 2017
C 80 2018
C 90 2019
I want to find max value of corresponding names which have Dates. If date is null that row should be deleted and the next corresponding value to be considered and displayed.
I have got max value but not able to delete that row

You can try below -
select name,max(val)
from tablename
where date is not null
group by name

Related

MySQL - Find start and end of blocks of consecutive rows with the same value

I need to extract and migrate values from one table to another. the source table contains sumarized values for a specific effectivity date. If a value is changed, a new line is written if something is changed on the component values with the data valid starting at this effective date.
source_id
entity_id
effective_date
component_1
component_2
component_3
int(ai)
int
date
int
int
int
1
159
2020-01-01
100
0
90
2
159
2020-05-01
140
50
90
3
159
2020-08-01
0
30
90
5
159
2020-12-01
0
30
50
i need now migrate this data to a new table like this. the goal is that selecting data for a given month the result is the valid data for this month is given.
id
source_id
entity_id
startdate
enddate
component_type
value
int(ai)
int
int
date
date
int
int
each row represents a value for a component valid for a period of month.
I now run the insert update for each effective month by setting it as a parameter.
I insert value changes as new rows to the table an prevent duplicates by using a unique key (entity_id,effective_date,component_type)
SET #effective_date = '2020-01-01';
INSERT INTO component_final
select NULL,
source_id,
entity_id,
effective_date,
NULL,
1,
component_1
FROM component_source
WHERE effective_date = #effective_date
AND component_1>0;
after migrating the first row it should be that result
id
source_id
entity_id
startdate
enddate
component_type
value
1
1
159
2020-01-01
NULL
1
100
2
1
159
2020-01-01
NULL
3
90
SET #effective_date = '2020-05-01';
INSERT INTO component_final
select NULL,
source_id,
entity_id,
effective_date,
NULL,
1,
component_1
FROM component_source
WHERE effective_date = #effective_date
AND component_1>0;
after migrating the second row it should be that result
id
source_id
entity_id
startdate
enddate
component_type
value
1
1
159
2020-01-01
2020-04-30
1
100
2
1
159
2020-01-01
NULL
3
90
3
2
159
2020-05-01
NULL
1
140
4
2
159
2020-05-01
NULL
2
50
so if there is a value change in the future an end date has to be set.
I'm not able to do the second step, updating the data, if the component is changed in the future.
Maybe it is possible to have it as triggers after insert new row with same entity and component - but I was not able to make it work.
Some ideas? I want to handle this only inside of the MySQL.
You do not need the column enddate in the table component_final, because it's value depends on other values in the same table:
SELECT
id,
source_id,
entity_id,
startdate,
( SELECT DATE_ADD(MIN(cf2.startdate),INTERVAL -1 DAY)
FROM component_final cf2
WHERE cf2.startdate > cf1.startdate
AND cf2.source_id = cf1.source_id
AND cf2.entity_id = cf1.entity_id
) as enddate,
component_type,
value
FROM component_final cf1;
I understand that the core issue is how to find the source_ids where a component changes (0 means a removal, so we don't want these entries in the result) and how to assign the respective end dates at the same time. For the sake of illustration I simplify your example a bit:
There is only one component_type (I take into account that there might then be consecutive entries with unchanged value)
there is only one entity_id, so we can ignore it
It should be easy to extend this simpler version to your real-world problem.
To this is an example input:
source_id
effective_date
value
1
2020-01-01
100
2
2020-01-03
100
3
2020-01-05
80
4
2020-01-10
0
5
2020-01-12
30
I would expect the following output to be generated:
source_id
start_date
end_date
value
1
2020-01-01
2020-01-04
100
3
2020-01-05
2020-01-09
80
5
2020-01-12
NULL
30
You can achieve this with one query by joing each row with the previous one to check if the value has changed (find the start dates of periods) and the first row that is in the future and has a different value (find the start of the next period). If there is no previous row, it is considered a start as well. If there is no later update of the value, we have no end_date.
SELECT
main.source_id,
main.effective_date as start_date,
DATE_SUB(next_start.effective_date, INTERVAL 1 DAY) as end_date,
main.value
FROM source main
LEFT JOIN source prev ON prev.effective_date = (
SELECT MAX(effective_date)
FROM source
WHERE effective_date < main.effective_date
)
LEFT JOIN source next_start ON next_start.effective_date = (
SELECT MIN(effective_date)
FROM source
WHERE effective_date > main.effective_date AND value <> main.value
)
WHERE
ISNULL(prev.source_id) OR prev.value <> main.value
AND main.value <> 0
ORDER BY main.source_id
As I said: This will have to be adapted to your problem, e.g. by adding proper join conditions for the entity_id.
#Luuk pointed out that you don't need the end date because it can be derived from the data. This would be the case if you had entries for the start of "0 periods" as well, i.e. if there is no value set. If you don't have entries for these, you can't derive the end from the start of the respectively next period since there might be a gap in between.

last entered value row in group by Mysql

My Query-
SELECT id, MAX(sl), details_sp FROM shareholderreceive GROUP BY id
and result
id MAX(sl) details_sp
1 76
2 74
3 64
4 67
5 69
6 70
10 72
But i need Those MAX(sl) Where details_sp column has last entered value.
My expected table is -
id MAX(sl) details_sp
1 72 Jul'16
2 74
3 64
4 62 Aug'16
5 69
6 70
10 71 Aug'16
here, data type details_sp is Varchar.
What query do I need to get this in MySQL?
What I think you mean is that when there is a details_sp filled in you need the sl value of that row and when there is no details_sp you need the max sl value?
If so, don't use MAX() but use the ORDER BY with the GROUP BY.
SELECT id, sl, details_sp
FROM shareholderreceive
ORDER BY details_sp DESC, sl DESC
GROUP BY id
The only problem with this is that you can never order by a date when it is a varchar value. This means you can never get the latest entry with just a varchar as a date.
So if there is a way, make the details_sp a date value (when you don't have a day just use the first day of the month, so 2017-06-01 for juli 2017) and use DATE_FORMAT() in MySQL or date_format in PHP (or JAVA or etc).

SSRS 2 column groups in matrix, 1 row reads from 1, the rest read from the other? (pls help)

Is there a way in SSRS to have an additional row within your row group, to look at a different column group than the rest of the row group
Let's say I have STATES, SALES, MONTH, and BUCKET_MONTH as my dataset fields BUCKET_MONTH is already calculated for me, based off of the MONTH. I want to show something like this:
SAMPLE DATA LIKE THIS FOR FLORIDA (and other months but BUCKET_MONTH only matters for florida let's pretend)
STATE MONTH SALES BUCKET_MONTH
FL JAN 50 FEB
FL FEB 125 FEB
FL MAR 100 MAY
FL APR 0 MAY
FL MAY 100 MAY
SSRS MATRIX MIGHT LOOK LIKE THIS: ?
| 2 groups ?
| MONTH
| BUCKET_MONTH (I can hide this header)
-----------------------------------
1 col group|
STATE | SALES
BUCKET | SALES <-- this row is only visibile for FL which I know how to do
EXPECTED RESULTS WOULD LOOK LIKE THIS
JAN FEB MAR APR MAY JUN JUL
---------------------------------------------------------------------
CA 100 300 150
FL 50 125 100 0 100
FL BUCKET 175 200 <-- BUCKET_MONTH**
MA 0 200 250 50
BUCKET_MONTH in ds shows FEB for the rows with Jan,Feb MONTH, and shows MAY for Mar,Apr, May MONTH
Is there a way to do this in SSRS? Where one of the rows looks at a different column group to establish what column to put the SUM of SALES in?
Much appreciation in advance!
You have to add BUCKET_MONTH as parent column group in your matrix.
Add BUCKET_MONTH in the Column Groups pane, then delete the created row in the matrix selecting Delete groups only option. Now add MONTH as child group in column groups pane.
Add STATE in rows group pane and add a row for bucket total.
Use this expression for BUCKET TOTAL:
=IIF(
Fields!BUCKET_MONTH.Value=Fields!MONTH.Value,
SUM(Fields!SALES.Value,"BUCKET_MONTH"),
Nothing
)
It should produce:
UPDATE: Expression updated taking in account that MONTH and BUCKET_MONTH fields are actually dates.
=IIF(
UCASE(format(Fields!BUCKET_MONTH.Value,"MMMM yy"))=
UCASE(format(Fields!MONTH.Value,"MMMM yy")),
SUM(Fields!SALES.Value,"BUCKET_MONTH"),
Nothing
)
Let me know if this helps.

Missing values on count in mysql

I'm just stuck with this issue atm and I'm not 100% sure how to deal with it.
I have a table where I'm aggregating data on week
select week(create_date),count(*)
from user
where create_date > '2015-02-01'
and id_customer between 9 and 17
group by week(create_date);
the results that I'm getting have missing values in the count, as shown below
5 334
6 376
7 394
8 405
9 504
10 569
11 709
12 679
13 802
14 936
15 1081
16 559
21 1
24 9
25 22
26 1
32 3
34 1
35 1
For example here from 16 to 21 there a obviously 4 values missing I would like these values to be included and count to be 0. I want this because I want the weeks to be matching with other metrics as we are outputting them in an excel file for internal analysis.
Any help would be greatly appreciated.
The problem is that an sql query cannot really produce data that is not there at all.
You have 3 options:
If you have data for each week in your entire table for the period you are querying, then you can use a self join to get the missing weeks:
select week(t1.create_date), count(t2.id_customer)
from customer t1
left join customer t2 on t1.id_customer=t2.id_customer and t1.create_date=t2.create_date and t2.id_customer between 9 and 17
where t1.create_date > '2015-02-01'
group by week(t1.create_date)
If you have missing weeks from the customer table as whole, then create a helper table that contain week numbers from 1 or 0 (depending on mysql config) to 53 and do a left join to this helper table.
Use a stored procedure that loops through the results of your original query and inserts the missing data in the resultset using a temporary table and then returns the extended dataset as result.
The problem is that there is no data matching your criteria for the missing weeks. A solution will be to join from a table that has all week numbers. For example if you create a table weeknumbers with one field weeknumber containing all the numbers from 0 to 53 you can use something like this
select weeknumber,count(user.*)
from weeknumbers left join user on (weeknumbers.weeknumber=week(user.create_date)
and user.create_date > '2015-02-01'
and user.id_customer between 9 and 17)
group by weeknumber;
Additionaly you might want to limit the week numbers you do not want to see.
The other way is to do it in the application.

How to get the right value using a join

This is driving me nuts, I'm sure it's rather simple but can't get it right.
Having a table holding ratevalues for different years:
Ratevaluetable
Year Value A Value B Value C
2009 10 15 20
2010 12 18 22
2011 14 21 25
Then I have another persontable:
User Time Price
john 2010 value B
Tina 2009 Value C
Bill 2011 Value C
Now what I need is to join the 2 tables so that I get the value in the Price column substituted with the data from the Ratevaluetable.
I can join on time on year, but the I don't know how to join to the Value fields?
This is not possible with your current database schema. Joins connect tables by matching column values; what you propose to do here is match a column value with a column name, which is impossible.
What would be possible is:
Table ValueTypes:
Id Description
1 Value A
2 Value B
3 Value C
Table RateValueTable:
Year ValueTypeId Value
2009 1 10
2009 2 15
2009 3 20
Table User:
User Time ValueTypeId
john 2009 2
Given the above schema, you could join User to RateValueTable in the obvious manner.
It is possible, but it's not really the best database design. You'd have to do a conditional statement to figure out which column of Ratevaluetable to get.
SELECT persontable.User, persontable.Time, CASE WHEN persontable.Price = 'value a' THEN `Ratevaluetable.Value A` WHEN persontable.Price = 'value b' THEN `Ratevaluetable.Value B` WHEN persontable.Price = 'value c' THEN `Ratevaluetable.Value C` AS 'price' FROM persontable, Ratevaluetable WHERE persontable.Time = Ratevaluetable.year