I have a dataset which returns the top 5 rows from a table and I display the data in SSRS Table. Now I need to maintain the 5 rows Table structure even if the number of rows returned by the dataset is 0 or less than 5. How can i acheive this.?
Thanks
A couple of possibilities.
1) You could add five Footer rows to the table and set the RowVisibility expression to = CountRows() > 0 through to = CountRows() > 4.
2) You could hack about with the query that populates the dataset such that it always returns exactly 5 rows.
e.g. Suppose your current query is
SELECT TOP (5) name,
create_date
FROM sys.objects
ORDER BY create_date
You could change that to
WITH TopFive
AS (SELECT TOP (5) name,
create_date,
ROW_NUMBER() OVER (ORDER BY create_date) AS RN
FROM sys.objects
ORDER BY create_date)
SELECT TF.name,
TF.create_date
FROM (VALUES(1),
(2),
(3),
(4),
(5)) V(N)
LEFT JOIN TopFive TF
ON TF.RN = V.N
ORDER BY TF.RN
Related
I am making a MySQL query of a table with thousands of records. What I'm really trying to do is find the next and previous rows that surround a particular ID. The issue is that when sorting the table in a specific way, there is no correlation between IDs (I can't just search for id > $current_id LIMIT 1, for example, because the needed ID in the next row might or might not actually be higher. Here is an example:
ID Name Date
4 Fred 1999-01-04
6 Bill 2002-04-02
7 John 2002-04-02
3 Sara 2002-04-02
24 Beth 2007-09-18
1 Dawn 2007-09-18
Say I know I want the records that come directly before and after John (ID = 7). In this case, the ID of the record after that row is actually a lower number. The table is sorted by date first and then by name, but there are many entires with the same date - so I can't just look for the next date, either. What is the best approach to find, in this case, the row before and (separately) the row after ID 7?
Thank you for any help.
As others have suggested you can use window functions for this, but I would use LEAD() and LAG() instead of ROW_NUMBER().
SELECT *
FROM (
SELECT
*,
LAG(ID) OVER (ORDER BY `Date` ASC, `Name` ASC) `prev`,
LEAD(ID) OVER (ORDER BY `Date` ASC, `Name` ASC) `next`
FROM `tbl`
) t
WHERE `ID` = 7;
With thousands of records (very small) this should be very fast but if you expect it to grow to hundreds of thousands, or even millions of rows you should try to limit the amount of work being done in the inner query. Sorting millions of rows and assigning prev and next values to all of them, just to use one row would be excessive.
Assuming your example of John (ID = 7) you could use the Date to constrain the inner query. If the adjacent records would always be within one month then you could do something like -
SELECT *
FROM (
SELECT
*,
LAG(ID) OVER (ORDER BY `Date` ASC, `Name` ASC) `prev`,
LEAD(ID) OVER (ORDER BY `Date` ASC, `Name` ASC) `next`
FROM `tbl`
WHERE `Date` BETWEEN '2002-04-02' - INTERVAL 1 MONTH AND '2002-04-02' + INTERVAL 1 MONTH
) t
WHERE `ID` = 7;
Without knowing more detail about the distribution of your data, I am only guessing but hopefully you get the idea.
You can use a window function called ROW_NUM in this way. ROW_NUM() OVER(). This will number every row in the table consecutively. Now you search for your I'd and you also get the Row number. For example, you search for ID=7 and you get row number 35. Now you can search for row number from 34 to 36 to get rows below and above the one with I'd 7.
This is what comes to mind:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY `date`, `name`) AS row_num
FROM people
) p1
WHERE row_num > (SELECT row_num FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY `date`, `name`) AS row_num
FROM people
) p2 WHERE p2.id = 7)
LIMIT 1;
Using the row number window function, you can compare two view of the table with id = 7 and get the row you need. You can change the condition in the subquery to suit your needs, e.g., p2.name = 'John' and p2.date = '2002-04-02'.
Here's a dbfiddle demonstrating: https://www.db-fiddle.com/f/mpQBcijLFRWBBUcWa3UcFY/2
Alternately, you can simplify the syntax a bit and avoid the redundancy using a CTE like this:
WITH p AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY `date`, `name`) AS row_num
FROM people
)
SELECT *
FROM p
WHERE row_num > (SELECT row_num FROM p WHERE p.id = 7)
LIMIT 1;
This is my query.
select trackingbatches.batchnumber, requireddate, jobno, frames.frame_id, frame_no, groupdesc, finishdesc, finish2desc, BOUGHTINFRAME, NAME, MAX(statustimestamp) from JOBQUOTEHEADER
right join trackingbatches on JOBQUOTEHEADER.header_id=trackingbatches.header_id
right join frames on trackingbatches.header_Id=frames.header_id
right join trackingstagesettings on trackingbatches.status=trackingstagesettings.stage_id
where requireddate between current_date-1 and current_date
GROUP BY 1,2,3,4,5,6,7,8,9,10
ORDER BY JOBNO
However, I do not want to group by 'name'. I want it to select the latest 'statustimestamp' but as of now, it groups by name also so it gives me a row which is the same frame with all the same details but multiple occurrences of it in the factory. name refers to the stage of the frame in our factory.
BATCHNUMBER
REQUIREDDATE
JOBNO
FRAME_ID
FRAME_NO
GROUPDESC
FINISHDESC
FINISH2DESC
BOUGHTINFRAME
NAME
STATUSTIMESTAMP
5079
01.09
5STAR1
1
1
INT CASEMENT
STD WHITE
N/A
0
CUT
16.08.2021
5079
01.09
5STAR1
1
1
INT CASEMENT
STD WHITE
N/A
0
LOADED
02.09.2021
So as you can see from the two records above. That is the same frame but i only want one instance of it to show in my results, the instance of the latest status of it which is the second one that is 'LOADED' as its status with the field called name. So i want the max status timestamp for the latest instance of the frame but because it groups by the field 'name' as well, i can't get just the latest instance by itself.
You can create a rank on your current query (when you see duplicates) something like the following:
Rank will create a row number for each row per batchnumber in the desc order for date
Once the rank is created you can just filter = 1 to get the data per batchnumer in desc order based on statustimestamp
This way you don't need to worry about NAME GROUP BY as it it will not be in the partition by clause of the ROW_NUMBER() [which is creating the rank based on just batchnumber -- you can adjust it by adding more columns if you need something else to aggregate by]
WITH data AS (
select trackingbatches.batchnumber, requireddate, jobno, frames.frame_id, frame_no, groupdesc, finishdesc, finish2desc, BOUGHTINFRAME, NAME, MAX(statustimestamp) from JOBQUOTEHEADER
right join trackingbatches on JOBQUOTEHEADER.header_id=trackingbatches.header_id
right join frames on trackingbatches.header_Id=frames.header_id
right join trackingstagesettings on trackingbatches.status=trackingstagesettings.stage_id
where requireddate between current_date-1 and current_date
GROUP BY 1,2,3,4,5,6,7,8,9,10
ORDER BY JOBNO
),
rank_data_by_date AS
SELECT
*
,
ROW_NUMBER() OVER(PARTITION BY BATCHNUMBER ORDER BY STATUSTIMESTAMP DESC) AS RANK_
)
SELECT * FROM rank_data_by_date WHERE RANK_ = 1
I have a table named "MI_APR". I am bringing in data from a "calendar" table using a LEFT JOIN. This data being transferred is a column of DATES with column name "First_Date".
ALTER TABLE MI_APR
ADD COLUMN `Profile` CHAR(8) NOT NULL DEFAULT 'MI',
ADD COLUMN First_Date DATE,
CHANGE `Location Label` `Label`TINYTEXT;
UPDATE MI_APR LEFT JOIN calendar on MI_APR.`Period` = calendar.`Unique_Period`
SET
MI_APR.`First_Date` = calendar.`First_Date`
After the join has happened, I need some help sorting the "FIRST_DATE" column in ASC , which is a simple date column in format YYYY-MM-DD.
I then need to add a new column named INDEX, that has values starting from 0 for the earliest month, counting upwards, based on the Month number in that DATES column in the MI_APR table. The index will run continue to run upwards even across years. See example below :
EXPECTED OUTPUT
FIRST_DATE INDEX
2018/10/01 0
2018/11/01 1
2018/12/01 2
2019/01/01 3
Thank you
You can either calculate your index on the fly with this query:
SELECT First_Date, TIMESTAMPDIFF(MONTH, earliest_date, First_Date) AS Index
FROM MI_APR
, (SELECT First_Date AS earliest_date FROM MI_APR ORDER BY First_Date ASC LIMIT 1) fancy_alias;
If the result pleases you, you can transform it into an update. My advice is though, that you shouldn't store data, which can easily be calculated on the fly!
UPDATE MI_APR
, (SELECT First_Date AS earliest_date FROM MI_APR ORDER BY First_Date ASC LIMIT 1) fancy_alias
SET `Index` = TIMESTAMPDIFF(MONTH, earliest_date, First_Date);
Regarding your comment "Also I would like the table to be sorted and then updated".
There is no order in a table you can rely on. If you want a result from a select sorted, use the ORDER BY clause.
I am having difficulty sorting results of multiple queries. The queries I'm running return a sorted list with zipcode as the key. So, for example, 1 query will return sorted zipcodes where crime rate is low, with lowest zipcode having a 1 as its index, then a query that returns zipcodes where average salary is over 100k or less with closest to 100k being index of 1.
Say I have 6 or more similar queries. How can I then sort zipcodes by sum of indices from all queries?
Example queries im running :
SELECT DISTINCT s1.Zip_Code, s1.Median_Value
FROM NJ_Housing_Expenses s1, NJ_Housing_Expenses s2
WHERE s1.Median_Value < 100000 AND s1.Zip_Code NOT IN (
SELECT Zip_Code
FROM NJ_Housing_Expenses
WHERE Median_Value = 0
)
ORDER BY Median_Value DESC
and
SELECT City, (((Violent_Crime*4) + Property_Crime)/Population) as CrimeSum
From NJ_Crime_Statistics
where Date = 2016
Group By City
Order by CrimeSum ASC
OUTPUT
1 08754
2 08234
3 07332
4 09563
then
1 08754
2 07332
3 09563
4 08234
Then is sorted by adding index
1 08754 (2)
2 07332 (5)
3 08234 (6)
4 09563 (7)
Sounds like you want to "number" the rows in each query. We could use a MySQL user-defined variable to do that.
We can wrap a suitable query in parens, and reference it as an inline view (in place of a table). As a demonstration.
SELECT q1.Zip_code
, #q1_rn := #q1_rn + 1 AS rn
FROM ( SELECT #q1_rn := 0 ) i
CROSS
JOIN (
-- source query here as inline view
SELECT s1.Zip_Code
, ...
FROM ...
ORDER BY Median_Value DESC
) q1
ORDER BY q1.Median_Value DESC
We can do the same thing for another query, but use a different user-defined variable
SELECT q2.Zip_code
, #q2_rn := #q2_rn + 1 AS rn
FROM ( SELECT #q2_rn := 0 ) i
CROSS
JOIN (
-- inline view query here
) q2
ORDER BY q2.CrimeSum ASC
We can combine the results of those queries with a UNION ALL set operator, and reference that whole thing as an inline view,
SELECT t.Zip_code
, SUM(t.rn) AS tot_rn
FROM (
(
-- first query from above goes here
)
UNION ALL
(
-- second query from above goes here
)
UNION ALL
(
-- third query
)
UNION ALL
(
-- fourth query
)
) t
GROUP BY t.Zip_code
ORDER BY tot_rn ASC
Add a GROUP BY to collapse all of the rows with the same Zip_Code (the first column returned by each of the source queries... each query should return exactly two columns... Zip_code and rn.
We use a SUM() aggregate to total up the values of rn, giving a total for each Zip_Code.
I've searched around SO and can't seem to find a question with an answer that works fine for me. I have a table with almost 2 million rows in, and each row has a MySQL Date formatted field.
I'd like to work out (in seconds) how often a row was inserted, so work out the average difference between the dates of all the rows with a SQL query.
Any ideas?
-- EDIT --
Here's what my table looks like
id, name, date (datetime), age, gender
If you want to know how often (on average) a row was inserted, I don't think you need to calculate all the differences. You only need to sum up the differences between adjacent rows (adjacent based on the timestamp) and divide the result by the number of the summands.
The formula
((T1-T0) + (T2-T1) + … + (TN-TN-1)) / N
can obviously be simplified to merely
(TN-T0) / N
So, the query would be something like this:
SELECT TIMESTAMPDIFF(SECOND, MIN(date), MAX(date)) / (COUNT(*) - 1)
FROM atable
Make sure the number of rows is more than 1, or you'll get the Division By Zero error. Still, if you like, you can prevent the error with a simple trick:
SELECT
IFNULL(TIMESTAMPDIFF(SECOND, MIN(date), MAX(date)) / NULLIF(COUNT(*) - 1, 0), 0)
FROM atable
Now you can safely run the query against a table with a single row.
Give this a shot:
select AVG(theDelay) from (
select TIMESTAMPDIFF(SECOND,a.date, b.date) as theDelay
from myTable a
join myTable b on b.date = (select MIN(x.date)
from myTable x
where x.date > a.date)
) p
The inner query joins each row with the next row (by date) and returns the number of seconds between them. That query is then encapsulated and is queried for the average number of seconds.
EDIT: If your ID column is auto-incrementing and they are in date order, you can speed it up a bit by joining to the next ID row rather than the MIN next date.
select AVG(theDelay) from (
select TIMESTAMPDIFF(SECOND,a.date, b.date) as theDelay
from myTable a
join myTable b on b.date = (select MIN(x.id)
from myTable x
where x.id > a.id)
) p
EDIT2: As brilliantly commented by Mikael Eriksson, you may be able to just do:
select (TIMESTAMPDIFF(SECOND,(MAX(date),MIN(date)) / COUNT(*)) from myTable
There's a lot you can do with this to eliminate off-peak hours or big spans without a new record, using the join syntax in my first example.
Try this:
select avg(diff) as AverageSecondsBetweenDates
from (
select TIMESTAMPDIFF(SECOND, t1.MyDate, min(t2.MyDate)) as diff
from MyTable t1
inner join MyTable t2 on t2.MyDate > t1.MyDate
group by t1.MyDate
) a