SQL: find intervals of step function - mysql

I have a table with 2 columns:
id INT PRIMARY_KEY
y FLOAT
The value in column "y" is guaranteed to be in 2 ranges:
small values: [0.18 - 0.20]
big values: [2.3 - 2.4]
It is known what "y" column in table has the following pattern: several records with small values then several records with big values then again several small and several big and so on. The number of consecutive records with the same range is not known and it is not fixed (vary). I need to find the id's (PK column) for beginning and ends of all intervals. Is it possible to do it in SQL?
(1; 0.19) (2; 0.18) (3; 0.19) (4; 2.3) (5; 2.4) (6; 2.3) (7;0.19)
Expect output: (1, 'start of small'), (3, 'end of of small'), (4, 'start of big'), (6, 'end of big'), (7, 'start of small')

Related

SSRS Show table list in sequence

I have a list of data, with sequence id 1, 2, 3, ..., 9
I want to insert them into SSRS report in the form of 3x3 table, arranged by sequence id
1 2 3
4 5 6
7 8 9
I should use list, or table, or matrix? Any solution for this arrangement? Thanks
I would typically use a matrix for this. You can calculate the row and column numbers for each row of data in your table and then use that for your row and column groups in the matrix.
Here's an example...
Note: You might not have or want the 'label' field so just swap this out for whichever field you want to show when we get to the report design.
Here's some sample data with the Row and Col calculated
DECLARE #t TABLE (Seq int, Label varchar(10))
INSERT INTO #t VALUES
(1, 'AAAAA'), (5, 'BBBBB'), (10, 'CCCCC'),
(20, 'DDDDD'), (50, 'EEEEE'), (100, 'FFFFF'),
(101, 'GGGGG'), (102, 'HHHHH'), (210, 'IIIII')
SELECT
*
, ColN = (SeqOrder-1) %3
, RowN = (SeqOrder-1) / 3
FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY Seq) AS SeqOrder FROM #t) t
The inner query just assigns the SeqOrder as 1 thru 9. We then use this SeqOrder value to determine the row and column
This gives us the following dataset
Then we just add a matrix to our report
Next, drag the ColN field to the "Columns" placeholder, the RowN field to the "Rows" placeholder and the Label field (or whatever field you want to display) to the "Data" placeholder.
Run the report and you get this
Optionally, you can remove the first row and column (but NOT the associated group) and just leave the data cell.
Now when we run it, we get this.

I attended a Hackerrank online test and I got this question, I couldn't solve it, Help me out if you can

Question's Image
I am not able to understand how to show the details for the same person for different dates, what do I group the data by for this to happen?
I have added an Image of the question do check the link out on top, it won't let me post embedded images as I am new to stackoverflow.
I have made a sample test case tables for the problem for your convinence.
Please help out if you can.
Create Table delivery
(
deliveryId int primary key,
delivery_date date,
De_Id int ,
Pickup_time time ,
delivery_time time
);
Insert Into delivery Values (450, '2020-04-17' , 111, '8:00', '9:00');
Insert Into delivery Values (451, '2020-04-17' , 111, '21:00', '23:00');
Insert Into delivery Values (452, '2020-04-17' , 111, '11:00', '11:30');
Insert Into delivery Values (453, '2020-04-17' , 112, '2:00', '3:35');
Insert Into delivery Values (454, '2020-04-17' , 112, '4:00', '4:40');
Insert Into delivery Values (455, '2020-04-17' , 112, '5:00', '7:00');
Insert Into delivery Values (456, '2020-04-18' , 111, '9:00', '11:00');
Insert Into delivery Values (457, '2020-04-18' , 111, '8:50', '9:55');
Insert Into delivery Values (458, '2020-04-18' , 111, '7:00', '9:06');
Insert Into delivery Values (459, '2020-04-18' , 112, '2:00', '3:35');
Insert Into delivery Values (460, '2020-04-18' , 112, '4:00', '4:40');
Insert Into delivery Values (461, '2020-04-18' , 112, '5:00', '7:00');
Create Table delivery_executive
(
ID int primary key,
Name varchar(20)
);
Insert into delivery_executive Values (111, 'Abby');
Insert into delivery_executive Values (112, 'Binto');
Here's one way to solve using MySql using row_number to order each delivery by the difference between the times, then filtering out all except the highest two, then using a conditional aggregate to pivot both rows with columns for each Id
with t as (
select deliveryid, de_id, delivery_date,
Row_Number() over(partition by delivery_date, de_id order by subtime(delivery_time,Pickup_time) desc)rn
from delivery
)
select e.Name, t.delivery_date,
Max(case when rn=1 then t.deliveryid end) FirstDeliveryId, Max(case when rn=2 then t.deliveryid end) SecondDeliveryId
from t
join delivery_executive e on e.id=t.de_id
where rn<=2
group by t.delivery_date, e.Name
How max and case help.
It helps to work through in stages. So firstly if we start with just what the CTE (the with t as ) element returns for the sample data, we see 12 rows - 6 for each date - each with 3 rows per de_id and a row number rn ordered 1-3 against each group with 1 having the longest time between the pickup and delivery times and 3 the shortest.
If we just look at select * from t where rn<=2, that naturally removes the the rows where rn=3 and we're left with the data we want, the two longest delivery time periods for each date, for each executive.
The desired output is to have just two rows per each executive, one for each date, and the two IDs to be in two columns.
Looking at case first, we can add two new columns as the result of a case expression,
case when rn=1 then t.deliveryId end,
case when rn=2 then t.deliveryId end
The first case will have a value only where rn=1 likewise the second case will have a value only where rn=2 (by default without an else it returns NULL on all other non-matched cases).
So now we have our First and Second Id values but they are still split over two rows.
The other values on each pair of rows (rn=1 and rn=2), are the same in each case, being the same delivery_date and the same de_id, so this is now where we can aggregate the rows together.
The final selected columns are the de_id and delivery_date and we want to group by these - ie all the values that are the same are condensed onto a single row provided the columns that we do not group by are aggregated in some way.
The most common aggregation would be sum, for example if we were only selecting the row number column we could sum(rn) and the result would be 3 for each aggregated group (1+2). However using max it will return the maximum value for each group of rows, and since we only have either a value (deliveryId) or NULL, it returns the deliveryId.
Therefore we wrap each case expression with a max() function, group by the remaining columns and the result is the grouped columns occupy a single row in each case since the aggregate of the case expressions also returns 1 row.
Fiddle here

Find app list which were added since last export for each device

My question deals with selecting items from a series that were not present in a previous observation. I am trying to adapt a technique from SQL: selecting rows where column value changed from previous row.
I have an application that includes a table that stores a list of apps installed on devices. Devices submit this data daily, and the full list is saved each day. I have been exporting new records each day for import into a reporting system but the data is growing very large because my reporting dataset has to retain a much larger time range that the actual application. And the way the app stores this data is not optimal for reporting. I would like to export only records that indicate that a new app has been installed on a device since the last time I performed a data export. The report_ids of last export and for the most recent report from the device are known. The data structure is not ideal. Essentially I am trying to convert a time series of data into a change log.
I don't own the source application so I can't change the database's data structures, triggers, etc.
*** Please see fiddle for data structure and sample data: http://sqlfiddle.com/#!9/ec6040/17
SQL:
-- Sample data:
CREATE TABLE last_exported_reports
(report_id int, mobile_device_id int);
INSERT INTO last_exported_reports
(mobile_device_id, report_id)
VALUES
(1, 1), (2, 6), (3, 7);
CREATE TABLE mobile_devices_denormalized
(mobile_device_id int, last_report_id int);
INSERT INTO mobile_devices_denormalized
(mobile_device_id, last_report_id)
VALUES
(1, 4), (2, 6), (3, 8);
CREATE TABLE reports
(`report_id` int, `mobile_device_id` int, `date_entered_epoch` bigint(32));
INSERT INTO reports
(`report_id`, `mobile_device_id`, `date_entered_epoch`)
VALUES
(1, 1, 1529981397691),
(2, 2, 1529981397692),
(3, 3, 1529981397693),
(4, 1, 1529981397694),
(5, 2, 1529981397695),
(6, 2, 1529981397696),
(7, 3, 1529981397697),
(8, 3, 1529981397698);
CREATE TABLE mobile_device_installed_applications
(`report_id` int, `identifier` varchar(8), `application_short_version` varchar(5));
INSERT INTO mobile_device_installed_applications
(`report_id`, `identifier`, `application_short_version`)
VALUES
(1, 'Chrome', 'c1.1'), -- device 1
(1, 'Word', 'w2.1'), -- device 1
(2, 'Skype', 's1.0'), -- device 2
(3, 'Excel', 'e3.0'), -- device 3
(4, 'Chrome', 'c2.1u'), -- device 1
(4, 'Word', 'w2.1n'), -- device 1
(4, 'Excel', 'w2.0'), -- device 1
(5, 'Skype', 's1.0'), -- device 2
(6, 'Skype', 's1.9'), -- device 2
(7, 'Excel', 'e3.0'), -- device 3
(8, 'Excel', 'e3.0'); -- device 3
SELECT
mdd.mobile_device_id AS md_id, mdia.report_id, mdia.identifier, mdia.application_short_version AS ver
FROM
-- List of all devices
mobile_devices_denormalized mdd
INNER JOIN
-- Add in apps
mobile_device_installed_applications mdia
ON
-- Only if they are from the last report from a device
mdia.report_id = mdd.last_report_id
AND
-- And only if the latest report has not already been exported
mdia.report_id NOT IN (select report_id FROM last_exported_reports)
AND
-- And only if the app in the new report was not in the last exported report
NOT EXISTS (
SELECT *
FROM mobile_device_installed_applications exported
WHERE exported.identifier = mdia.identifier
AND exported.report_id = mdd.last_report_id
)
;
If I run the above without the NOT EXISTS clause, it works fine in that I get all app records for new reports as expected. When I add in the final step, the subquery to get rid of repetative apps, I get no results.
RESULT IF RUN WITHOUT THE SUBQUERY CLAUSE:
md_id report_id identifier ver
1 4 Chrome c2.1u
1 4 Word w2.1n
1 4 Excel w2.0
3 8 Excel e3.0
This is correct for device 1 because report 4 is most
recent and is not the previously exported report (4 != 1)
This is correct for device 2 because report 6 is most
recent but it was already exported so no records are exported.
This is correct for device 3 because report 8 is most
recent and is not the previously exported report (8 != 7)
RESULT WHEN I ADD IN THE FINAL SUBQUERY CLAUSE:
No records.
EXPECTED RESULT:
md_id report_id identifier ver
1 4 Excel w2.0
The above record should have been printed because it is in
the most recent report (report_id=4) for device #1 and the app was not on the device the last time it was exported (report_id=1).
SELECT a.*
FROM (
SELECT
mdd.mobile_device_id,
mdia.report_id,
mdia.identifier,
mdia.application_short_version AS ver
FROM mobile_devices_denormalized mdd
INNER JOIN mobile_device_installed_applications mdia ON mdia.report_id = mdd.last_report_id
) AS a
LEFT JOIN (
SELECT
ler.mobile_device_id,
mdia.report_id,
mdia.identifier,
mdia.application_short_version AS ver
FROM last_exported_reports ler
INNER JOIN mobile_device_installed_applications mdia ON mdia.report_id = ler.report_id
) AS b ON a.mobile_device_id = b.mobile_device_id AND a.identifier = b.identifier
WHERE b.report_id IS NULL;
Logic: Match new report app list with app list from last exported report for the same device and select which are not matching from new report.

SSRS - avg function by subtotals

I have details, subtotals and totals.
When I put avg function in totals line I have avg of every row.
I need avg of subtotals
How to do it?
week1
day1..... 2
day3..... 3
day4..... 4
day6..... 2
total.... 11 sum()
week2
day1..... 3
day2..... 2
total..... 5 sum()
Total
........... 16 sum() OK
............ 2,66666 avg() here should be (11+5)/2 =8
Result after implementing solution
I created a dataset to replicate your sample data as follows:
DECLARE #t TABLE (week int, day int, amount int)
INSERT INTO #t VALUES
(1, 1, 2),
(1, 3, 3),
(1, 4, 4),
(1, 6, 2),
(2, 1, 3),
(2, 2, 2)
SELECT * FROM #t
I then built a simple tablix as you had done (more or less)
I included the incorrect results you had for illustration and then added a new expression to calculate this correctly.
The result looks like this
You can ignore the other datasets, this is just a report I use for testing. Only dataset3 is used here.
The expression used was this
=Sum(Fields!amount.Value) / CountDistinct(Fields!week.Value)
You'll just need to edit this to match your field names. It basically just sums all the detail amounts then divides by the number of distinct weeks in the dataset.

storing allowed settings combinations

I am making an interface where a user can define settings. The settings are separated in categories and the user can only select one parameter from each category. The thing is, only certain combinations of parameters are allowed and I must prevent the users from selecting incompatible parameters.
I am trying to design the mysql database structure for that but I did not find a solution that satisfies me.
What I thought of is :
Categories
- ID
- Description
Parameters
- ID
- Parent Category ID
- Description
Combinations
-ID
- a string that is the concatenation of parameters IDs ordered by category
eg. : 102596 would be the combination of parameters 10 from category 1, 25 from category 2, and 96 from category 3.
The problems is : what if one day I need more than a hundred parameters ?
Ok So you have these tables:
Categories
- ID
- Description
And each category has multiple parameters:
Parameters
- ID
- Parent Category ID
- Description
Now you want to store the parameters that the user has selected. Why dont you handle the operation of permitting of selecting only one parameter per category outside of the database design scope? If you can do that, then the combination table would be simple:
Combinations
- ID
- USER_ID
- CATEGORY_ID
- PAREMETER_ID
You should probably use a table for grouping combinations, as such:
Combinations
- ID
- Group_ID
- Parameter_ID
So for instance, let's use your example of the allowed combination 10, 25 and 96, you would have this 3 entries in the "Combinations" table:
(ID, Group_ID, Parameter_ID) = (1, 1, 10), (2, 1, 25), (3, 1, 96)
(the IDs can be auto-generated ones)
So that means you have this group (1) of 3 allowed parameters (10, 25, 96).
then if you want to add another possible combination of, say, 15, 16, 23 and 42:
(ID, Group_ID, Parameter_ID) = (4, 2, 15), (5, 2, 16), (6, 2, 23), (7, 2, 42)
(again the IDs can be auto-generated ones)
So that means you have this group (2) of 4 allowed parameters (15, 16, 23, 42).