Consider this data (6 rows and 1 column for now)
;with cols as
(
SELECT 1 colID, 'C1' col
--UNION SELECT 2, 'C2'
)
, rows as
(
SELECT 1 RowID, 'R1' row, null ParentID
UNION SELECT 2, 'R2', 1
UNION SELECT 3, 'R3', 2
UNION SELECT 4, 'R4', 2
UNION SELECT 5, 'R5', 1
UNION SELECT 6, 'R6', 1
)
,data
AS
(
SELECT 3 RowID, 1 as Amount
UNION SELECT 4 RowID, 2 as Amount
)
SELECT r.RowID, r.row, c.colID, c.col, d.Amount, r.ParentID
FROM rows r
CROSS JOIN cols c
LEFT JOIN data d on d.RowID = r.RowID
I apply this to a matrix control using the following layout and get the output as shown. Notice how the amounts are not rolled up to the parent rows. THis is ok for now.
Now, to get the values to roll up I can use the expression (well documented by msdn, blogs, etc)
=Sum(Fields!Amount.Value, "RowGroup", recursive)
This now gives me exactly what I want, with the values rolling up to their parent rows:
However, my dataset has dynamic column as well as rows but when a second (or third, forth, etc) column is introduced the recursive sum doesn't work as I expect. Instead of staying within the scope of the current column it sums all the columns and then rolls those values up to the parent lines. as shown:
I want the values to only get rolled up within the current scope of a given row and column.
Any guidance would be greatly appreciated as this has stumped me.
Thanks
This isn't actually possible due to needing to look at both the row group scope and the column group scope..
I spent a long time searching for an answer.. For anyone else looking. here are some better explanations from other forums:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/55c55aad-4755-4da5-afce-94d16c9e201d/cannot-perform-a-correct-recursive-sum-in-a-tablix?forum=sqlreportingservices
Unfortunately, the scenario you described is not supported so far (both 2008 and 2005), because it cannot do Recursive sum in two scopes in a matrix. If you replace the expression with SUM(Fields!Amount.Value, "NAME", Recursive), you will get the recursive total, but the tota willl ignore the monthindex group. Actually, you want to recursive sum for both groups NAME and MonthIndex, however you cannot write expression like this: SUM(Fields!Amount.Value, "NAME" and MonthIndex, Recursive). Right? So generally speaking, in a matrix, when you write the expression like this: SUM(Fields!Amount.Value) It will sum in the scope of two groupsNAME and MonthIndex, and if you want to specify the scope, you have to specify only one scope. And unfortunately, the recursive sum needs to specify the scope and scenario you described needs to specify two scopes
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/9d7f36bc-73d6-4f19-a306-0b84321e6feb/calculating-the-sum-on-a-hierarchy-with-multiple-grouping-columns?forum=sqlreportingservices
Related
I have a table that has a new column, and updating the values that should go in the new column. For simplicity sake I am reducing my example table structure as well as my query. Below is how i want my output to look.
IDNumber NewColumn
1 1
2 1
3 1
4 2
5 2
WITH CTE_Split
AS
(
select
*,ntile(2) over (order by newid()) as Split
from TableA
)
Update a
set NewColumn = a.Split
from CTE_Split a
Now when I do this I get my table and it looks as such
IDNumber NewColumn
1 1
2 1
3 1
4 1
5 1
However when I do the select only I can see that I get the desire output, now I have done this before to split result sets into multiple groups and everything works within the select but now that I need to update the table I am getting this weird result. Not quiet sure what I'm doing wrong or if anyone can provide any sort of feedback.
So after a whole day of frustration I was able to compare this code and table to another that I had already done this process to. The reason that this table was getting updated to all 1s was because turns out that whoever made the table thought this was supposed to be a bit flag. When it reality it should be an int because in this case its actually only two possible values but in others it could be more than two.
Thank you for all your suggestion and help and it should teach me to scope out data types of tables when using the ntile function.
Try updating your table directly rather than updating your CTE. This makes it clearer what your UPDATE statement does.
Here is an example:
WITH CTE_Split AS
(
SELECT
*,
ntile(2) over (order by newid()) as Split
FROM TableA
)
UPDATE a
SET NewColumn = c.Split
FROM
TableA a
INNER JOIN CTE_Split c ON a.IDNumber = c.IDNumber
I assume that what you want to achieve is to group your records into two randomized partitions. This statement seems to do the job.
Why are column ordinals legal for ORDER BY but not for GROUP BY? That is, can anyone tell me why this query
SELECT OrgUnitID, COUNT(*) FROM Employee AS e GROUP BY OrgUnitID
cannot be written as
SELECT OrgUnitID, COUNT(*) FROM Employee AS e GROUP BY 1
When it's perfectly legal to write a query like
SELECT OrgUnitID FROM Employee AS e ORDER BY 1
?
I'm really wondering if there's something subtle about the relational calculus, or something, that would prevent the grouping from working right.
The thing is, my example is pretty trivial. It's common that the column that I want to group by is actually a calculation, and having to repeat the exact same calculation in the GROUP BY is (a) annoying and (b) makes errors during maintenance much more likely. Here's a simple example:
SELECT DATEPART(YEAR,LastSeenOn), COUNT(*)
FROM Employee AS e
GROUP BY DATEPART(YEAR,LastSeenOn)
I would think that SQL's rule of normalize to only represent data once in the database ought to extend to code as well. I'd want to only right that calculation expression once (in the SELECT column list), and be able to refer to it by ordinal in the GROUP BY.
Clarification: I'm specifically working on SQL Server 2008, but I wonder about an overall answer nonetheless.
One of the reasons is because ORDER BY is the last thing that runs in a SQL Query, here is the order of operations
FROM clause
WHERE clause
GROUP BY clause
HAVING clause
SELECT clause
ORDER BY clause
so once you have the columns from the SELECT clause you can use ordinal positioning
EDIT, added this based on the comment
Take this for example
create table test (a int, b int)
insert test values(1,2)
go
The query below will parse without a problem, it won't run
select a as b, b as a
from test
order by 6
here is the error
Msg 108, Level 16, State 1, Line 3
The ORDER BY position number 6 is out of range of the number of items in the select list.
This also parses fine
select a as b, b as a
from test
group by 1
But it blows up with this error
Msg 164, Level 15, State 1, Line 3
Each GROUP BY expression must contain at least one column that is not an outer reference.
There is a lot of elementary inconsistencies in SQL, and use of scalars is one of them. For example, anyone might expect
select * from countries
order by 1
and
select * from countries
order by 1.00001
to be a similar queries (the difference between the two can be made infinitesimally small, after all), which are not.
I'm not sure if the standard specifies if it is valid, but I believe it is implementation-dependent. I just tried your first example with one SQL engine, and it worked fine.
use aliasses :
SELECT DATEPART(YEAR,LastSeenOn) as 'seen_year', COUNT(*) as 'count'
FROM Employee AS e
GROUP BY 'seen_year'
** EDIT **
if GROUP BY alias is not allowed for you, here's a solution / workaround:
SELECT seen_year
, COUNT(*) AS Total
FROM (
SELECT DATEPART(YEAR,LastSeenOn) as seen_year, *
FROM Employee AS e
) AS inline_view
GROUP
BY seen_year
databases that don't support this basically are choosing not to. understand the order of the processing of the various steps, but it is very easy (as many databases have shown) to parse the sql, understand it, and apply the translation for you. Where its really a pain is when a column is a long case statement. having to repeat that in the group by clause is super annoying. yes, you can do the nested query work around as someone demonstrated above, but at this point it is just lack of care about your users to not support group by column numbers.
I am looking to retrieve only the second (duplicate) record from a data set. For example in the following picture:
Inside the UnitID column there is two separate records for 105. I only want the returned data set to return the second 105 record. Additionally, I want this query to return the second record for all duplicates, not just 105.
I have tried everything I can think of, albeit I am not that experience, and I cannot figure it out. Any help would be greatly appreciated.
You need to use GROUP BY for this.
Here's an example: (I can't read your first column name, so I'm calling it JobUnitK
SELECT MAX(JobUnitK), Unit
FROM JobUnits
WHERE DispatchDate = 'oct 4, 2015'
GROUP BY Unit
HAVING COUNT(*) > 1
I'm assuming JobUnitK is your ordering/id field. If it's not, just replace MAX(JobUnitK) with MAX(FieldIOrderWith).
Use RANK function. Rank the rows OVER PARTITION BY UnitId and pick the rows with rank 2 .
For reference -
https://msdn.microsoft.com/en-IN/library/ms176102.aspx
Assuming SQL Server 2005 and up, you can use the Row_Number windowing function:
WITH DupeCalc AS (
SELECT
DupID = Row_Number() OVER (PARTITION BY UnitID, ORDER BY JobUnitKeyID),
*
FROM JobUnits
WHERE DispatchDate = '20151004'
ORDER BY UnitID Desc
)
SELECT *
FROM DupeCalc
WHERE DupID >= 2
;
This is better than a solution that uses Max(JobUnitKeyID) for multiple reasons:
There could be more than one duplicate, in which case using Min(JobUnitKeyID) in conjunction with UnitID to join back on the UnitID where the JobUnitKeyID <> MinJobUnitKeyID` is required.
Except, using Min or Max requires you to join back to the same data (which will be inherently slower).
If the ordering key you use turns out to be non-unique, you won't be able to pull the right number of rows with either one.
If the ordering key consists of multiple columns, the query using Min or Max explodes in complexity.
May I know is there any solution to get the result without ordering in Oracle? It is because when I execute the query as follows, it seems to automatically helps me to sort it by ID field.
SELECT ID FROM USER WHERE ID IN (5004, 5003, 5005, 5002, 5008);
Actual results Expected results
---5002 ---5004
---5003 ---5003
---5004 ---5005
---5005 ---5002
---5008 ---5008
Million thanks if you guys have solutions on this.
SELECT statements return the rows of their result sets in an unpredictable order unless you give an ORDER BY clause.
Certain DBMS products give the illusion that their result sets are in a predictable order. But if you rely on that you're bound to be disappointed.
This is one way I've seen in the past using INSTR:
SELECT *
FROM YourTable
WHERE ID IN (5004, 5003, 5005, 5002, 5008)
ORDER BY INSTR ('5004,5003,5005,5002,5008', id)
SQL Fiddle Demo
I've also seen use of CASE like this:
ORDER BY
CASE ID
WHEN 5004 THEN 1
WHEN 5003 THEN 2
WHEN 5005 THEN 3
WHEN 5002 THEN 4
WHEN 5008 THEN 5
END
if you want to keep the order as your in list, you can do something like this:
SQL> create type user_va as varray(1000) of number;
2 /
Type created.
SQL> with users as (select /*+ cardinality(a, 10) */ rownum r, a.column_value user_id
2 from table(user_va(11, 0, 19, 5)) a)
3 select d.user_id, d.username
4 from dba_users d
5 inner join users u
6 on u.user_id = d.user_id
7 order by u.r
8 /
USER_ID USERNAME
---------- ------------------------------
11 OUTLN
0 SYS
19 DIP
5 SYSTEM
i.e we put the elements into a varray and assign a rownum prior to merging the set. we can then order by that r to maintain the order of our in list. The cardinality hint just tells the optimizer how many rows are in the array (doesn't have to be dead on, just in the ballpark..as without this, it will assume 8k rows and may prefer a full scan over an index approach)
if you don't have privs to create a type and this is just some adhoc thing, there's a few public ones:
select owner, type_name, upper_bound max_elements, length max_size, elem_type_name
from all_Coll_types
where coll_type = 'VARYING ARRAY'
and elem_type_name in ('INTEGER', 'NUMBER');
There is no guarantee of sort order without an ORDER BY clause.
If your question is about why the ordering occurs then the answer is: Do you have an index or primary key defined on the column ID? If yes the database responds to your query with an index scan. That is: it looks up the IDs in the IN clause not in the table itself but in the index defined on your ID-column. Within the index the values are ordered.
To get more information about the execution of your query try Oracle's explain plan feature.
To get the values in a certain order you have to add an ORDER BY clause. One way of doing this would be
select ID
from USER
where ID in (5004, 5003, 5005, 5002, 5008)
order by
case ID
when 5004 then 1
when 5003 then 2
...
end;
A more general way would be to add an ORDERING column to your table:
select ID
from USER
where ID in (5004, 5003, 5005, 5002, 5008)
order by
ORDERING;
Another solution that I found here.
select ID
from USER
where ID in (5004, 5003, 5005, 5002, 5008)
order by decode(ID, 5002, 1, 5003, 2, 5004, 3, 5005, 4, 5008, 5);
order by decode(COLUMN NAME, VALUE, POSITION)
*Note: Only need to repeat the VALUE and POSITION
And yah, thanks for all the responds! I am really appreciate it.
In SSRS 2005, is there a way I can create two side by side tables of 6 rows each so that each table (or Matrix) has a portion of the same data set?
Here is the query I'm using for the dataset.
Select
Equipment_EquipmentID,
Equipment_Description,
Vendor,
Operator,
JobAccount_TrackingID
from
(
Select row_number() over (partition by Equipment_EquipmentID order by JobAccount_TrackingID) row,*
from
(
SELECT DISTINCT
CustomizedFieldLogEquipmentJobAccount_1.Equipment_EquipmentID,
CustomizedFieldLogEquipmentJobAccount_1.Equipment_Description,
NULL as Vendor,
NULL as Operator,
CustomizedFieldLogEquipmentJobAccount_1.JobAccount_TrackingID
FROM
CustomizedFieldLogEquipmentJobAccount AS CustomizedFieldLogEquipmentJobAccount_1 INNER JOIN CustomizedAccount ON CustomizedFieldLogEquipmentJobAccount_1.Account_AccountID = CustomizedAccount.AccountID
WHERE
(CustomizedAccount.AppliesToEquipment = 1)
AND
(CustomizedFieldLogEquipmentJobAccount_1.FieldLog_FieldLogID =#FieldLogID)
--AND
--IsRented = 0
Union
Select Distinct
Equipment_EquipmentID,
Equipment_Description,
Null as Vendior,
NULL as Operator,
fake_TrackingID
FROM
CustomizedFieldLogEquipment
cross join
(
Select 'zzz01' fake_TrackingID
union Select 'zzz02'
union Select 'zzz03'
union Select 'zzz04'
union Select 'zzz05'
union Select 'zzz06'
union Select 'zzz07'
union Select 'zzz08'
union Select 'zzz09'
union Select 'zzz10'
) a where FieldLog_FieldLogID = #FieldLogID --AND IsRented = 0
) b
) c where Row<=6
order by Equipment_EquipmentID,JobAccount_TrackingID
The TrackingiD is the column in my Matrix and I need to show a total of 6 columns weather or not there is data in there (Hence the fake TrackingIds) the query works great. But I need to be able to split the results of the query between two matrix controls in the report if the rowcount is greater than 6.
In essence I need 2 6x6 tables. The way it needs to work is strange. If the query results return more than 6 rows, the users want the rest of the records to flow over to the 2nd table. Kind of like the illustration below:
From this:
To this:
I've already gotten the two matrix controls on the form, but can't figure out how to split the dataset between two tables like that if the row count is larger than 7 or so.
Is this possible? If so how? I've done some searching, but can't figure out how to split a single recordset into two matrix controls. Any help is appreciated. I keep trying to tell the users that this is not a form printing program but a program designed to report what is in the database.
Thanks
Add a RowNumber column to your dataset.
Then add a filter to each matrix control. The first matrix should filter for rows with RowNumber between 1-6, and the second matrix would filter for 7-12.
Admittedly, this is a quick and dirty way. The report itself however is outside the intended use of SSRS, so I don't know if it's really worth worrying about how you get the matrices to split, as long as there aren't any performance issues.
This seems so limiting and inflexible. Are the users really sure they only want x number of rows, forever? If they ever change the number of rows, you're the victim who has to make changes to the report to accommodate.