How can I display Row Number in Cross-Tab? - visual-studio

I'm looking for a way to display row number in my cross-tab.
I tried searching online for the answer on how to do it but I haven't found anything useful.
So I'm turning to the good people on Stack Overflow.
The reason that I want to do this, if it's even possible, is because many clients in the company I started working at asked to have a row number in the cross-tab.
I am using Visual Studio 2013 and Crystal Reports.
So is there any basic ( easy ) way to do this in Crystal Reports?
For example, I have a cross-tab that displays unit of measure and amounts.
https://imgur.com/a/lOjCq
But I would like my cross-tab to be like:
Amount
1. Total -38
2. KG
3. kut 9
4. LIT. 4
5. m -32
6. proc
7. KoŠ¼ -19
Please keep in mind that I only started working with Crystal Reports this week, so this is all new to me. And the cross-tab in the picture is just a random one I made to explain what I need.
Thank You in advance.

In order to show Row Number in your CrossTab you will need to first put Row Number in the Stored Procedure that sends data to your report.
In order to understand it better i will first show you how my data looks before i add a Row number(Pic 1).
Code:
select
a.S_ID as ID,
osn.sifra as BasicGoodsCode,
osn.naziv as BasicGoods,
null,
a.RobaSifra as GoodsCode,
a.Roba as Goods,
a.Detalj as Detail,
a.DetaljDodatak as DetailsAddon
from NP_Stavke s
left join RobaGrupe osn on osn.id = s.RobaId
left join #A a on a.S_ID = s.Id
order by BasicGoodsCode, ID
Pic 1: As you can see I have 3 different Ids for BasicGoods which means that I have 3 Rows in my CrossTab
Columns ID, BasicGoodsCode and BasicGoods are going to be Rows in my CrossTab.
Values from column DetailsAddon are going to be my columns in CrossTab.
Columns GoodsCode, Goods and Detail are going to be values in my CrossTab.
Column Pieces is not important.
Now that you know how everything looks we can start with adding a Row Number to our CrossTab.
Step 1:
First thing that you need to do is to add a Row number in table in your stored procedure.
To do this I used DENSE_RANK()
depending on your data you might need to use ROW_NUMBER() or maybe even something else. I used DENSE_RANK() because I needed my row number to change once S_ID changes.
Code:
select
a.S_ID as ID,
DENSE_RANK() OVER (ORDER BY osn.sifra, s.Id asc) as BasicGoodsRowNo, // THIS IS ADDED
osn.sifra as BasicGoodsCode,
osn.naziv as BasicGoods,
null as Pieces,
a.RobaSifra as GoodsCode,
a.Roba as Goods,
a.Detalj as Detail,
a.DetaljDodatak as DetailsAddon
from NP_Stavke s
left join RobaGrupe osn on osn.id = s.RobaId
left join #A a on a.S_ID = s.Id
order by BasicGoodsCode, ID
Lets take a look at how our data looks now(Pic 2)
As you can see we added a Row Number that changes when Id changes.
IMPORTANT: Row Number has to be ether Integer or Decimal in the DataTable that you are using in your report if it's not it will not work correctly.
Step 2:
We've done the 'hard' part now it's time to put Row number in our CrossTab.
When you create a CrossTab or when right click CrossTab and then click on 'Cross-Tab Expert...' it will open a window like this one and in it in the Row section you will insert your Row Number Column(in my case and as you can see in the code above the name of my Row Number Column is 'BasicGoodsRowNo').
Step 3:
Since you don't want to show only the Row number in the report left click on your Row and then click on 'Group Options...'(Pic 4)
Once the new window appears click on 'Options' tab then check the 'Customise group name field' then click on 'Use a formula as group name' and then on 'x-2'(Pic 5)
Step 4:
Enter a formula like this one:
toText( {myTbl.BasicGoodsRowNo}, 0, "" ) + '. ' + {myTbl.BasicGoodsCode} + ' ' + {myTbl.BasicGoods}
Of course your formula will not be exactly like mine since you will not have the same columns as I do. The only part of this formula that you HAVE to have is toText( {myTbl.BasicGoodsRowNo}, 0, "" ) where instead of {myTbl.BasicGoodsRowNo} you will put your row number column. You will need toText since if you dont have that and you want to show a String after your Row Number it will give you an error because RowNumber is an integer field.
GJ YOU ARE ALL DONE AND IT WASN'T THAT HARD WAS IT
How My CrossTab looks once RowNumber is added
Now there is a way to simplify this process and that is:
Step 1:
In your stored procedure create 2 columns. One will show Row Number and the other will show Value that will be displayed as CrossTab row.
Code:
select
a.S_ID as ID,
DENSE_RANK() OVER (ORDER BY osn.sifra, s.Id asc) as BasicGoodsRowNo, // RowNumber
CONVERT(varchar(10), DENSE_RANK() OVER (ORDER BY osn.sifra, s.Id asc)) + '. ' + osn.sifra + ' ' +osn.naziv as BasicGoods, // Value that will be displayed in CrossTab Row
null as Pieces,
a.RobaSifra as GoodsCode,
a.Roba as Goods,
a.Detalj as Detail,
a.DetaljDodatak as DetailsAddon
from NP_Stavke s
left join RobaGrupe osn on osn.id = s.RobaId
left join #A a on a.S_ID = s.Id
order by BasicGoods, ID
As you can see Column BasicGoodsRowNo did not change and will still display the same values as before and I have deleted the clumns BasicGoodsCode and BasicGoods and replaced them with this
CONVERT(varchar(10), DENSE_RANK() OVER (ORDER BY osn.sifra, s.Id asc)) + '. ' + osn.sifra + ' ' +osn.naziv as BasicGoods,
The BasicGoods column will show BasicGoodsRowNo + BasicGoodsCode + BasicGoods.
Step 2:
Step 2 is the same as before.
Step 3:
Once you click on your row and on 'Group Options' go to 'Options' tab again then check the 'Customise group name field' check box again and after that instead clicking on 'Use a formula as group name' click on 'Choose from existing field' and from a combo box select the column you want to show as Row Value in your CrossTab. In my Case that is 'BasicGoods' column (Pic 7).
I used the first method since depending on what user decides I may not show CrossTab at all and I may not show BasicGoods but if you only have CrossTab in your report you can use the second, shorter and easier, method.
If you have any questions feel free to ask.

Related

Generate order number based on a column value with reference to other columns

This is question in Oracle views.I have a table with Emp_id,Start_Period and Key. Sample data is given in Descending order of start period with 201909 on top. Need to generate a column named Key_order. (Finally I am planning to create a view with all 4 columns.)
With the sample data as shown. In the sorted list with Start_period what ever comes in first position with number 1 and then on, when the Key changes order has to increment by one.
That is in row 1 and 2 key is same and order is 1. In row 3 SCD changed to ABC so order has to increment by 1 so order value is 2. 4th position key changes and order becomes 3.
See in 7th and 8th position value is same so order remains 6 for both. I am trying to do this inside a view. Tried RANK() but it is sorting column Key and giving order based on that.
Please help.Sample Data
Set a one in each line that has a different key than the line before. Use LAG for this. Then build a running total of these ones with SUM OVER.
select
emp_id, start_period, key,
sum(chg) over (partition by emp_id order by start_period desc) as key_order
from
(
select
emp_id, start_period, key,
case when key = lag(key) over (partition by emp_id order by start_period desc)
then 0 else 1 end as chg
from mytable
)
order by emp_id, start_period desc;

Type wise summation and subtracting in oracle

I have two table of my store and working on Oracle. Image First table describe about my transaction in store, there are two types of transaction (MR & SR), MR means adding products in Store and SR means removing products from my storage. What I wanted to do get the final closing of my storage. After transaction final Quantity every products as shown in Image. I have tried many solution but can't finish it. so I could not show now. Please help me to sort this problem. Thanks
You can use case as below to decrease and increase the quantity based on type and then group by Name and find the sum of quantity derived from the case statement to get your desired result.
select row_number() over (order by a.Name) as Sl,a.Name, sum(a.qntity) as qntity
from
(select t2.Name,case when t1.type='MR' then t2.qntity else -(t2.qntity) end as qntity
from table1 t1,table2 t2 where t1.oid=t2.table01_oid) a
group by a.Name;
This query will provide result as below:
SL NAME QNTITY
1 Balls 0
2 Books 6
3 Pencil 13

Seeking hints to simplify complex query

Building a web page to show summary data and a chart. The query to obtain my summary data appears to be overly complex and there must be a simpler way to accomplish. I'm mainly experienced with SQL Server, and under SQL Server, getting row and column level totals is done within the main query. No unions or sub queries required, unless you are doing some more complex things.
However, under Oracle 10g, this appears to be the way to accomplish the same thing.
The resulting data is put into a JSON array and populates a v1.10 DataTable.
The source data has a row containing the date, item and a count of items.
The ending table uses a pivot, becoming 8 columns, 6 for the items, a date and row-level total. I trimmed 2 columns to simplify reduce the clutter in the question. The final row has column-level totals and the final grand total. Any suggestions welcome.
The query is here
SELECT *
FROM (
SELECT TO_CHAR("DATE", 'MM/DD/YYYY') AS "DATE"
, ITEM_NAME
, SUM(ITEM_COUNT) AS TOTAL
FROM MY_VIEW
WHERE 1=1
AND "DATE" > ADD_MONTHS(TO_DATE(SYSDATE, 'DD-MM-RR'), -1)
AND ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04')
GROUP BY "DATE", ITEM_NAME
UNION ALL
SELECT TO_CHAR("DATE", 'MM/DD/YYYY') AS "DATE"
, 'ROW_TOTAL' AS ITEM_NAME
, SUM(ITEM_COUNT) AS TOTAL
FROM MY_VIEW
WHERE 1=1
AND "DATE" > ADD_MONTHS(TO_DATE(SYSDATE, 'DD-MM-RR'), -1)
AND ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04')
GROUP BY "DATE"
)
PIVOT
(
MAX(TOTAL) FOR ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04','ROW_TOTAL')
)
UNION ALL
SELECT *
FROM (
SELECT 'GRAND TOTAL' AS "DATE"
, ITEM_NAME
, SUM(ITEM_COUNT) AS TOTAL
FROM MY_VIEW
WHERE 1=1
AND "DATE" > ADD_MONTHS(TO_DATE(SYSDATE, 'DD-MM-RR'), -1)
AND ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04')
GROUP BY ITEM_NAME
UNION ALL
SELECT 'GRAND TOTAL' AS "DATE"
, 'ROW_TOTAL' AS ITEM_NAME
, SUM(ITEM_COUNT) AS TOTAL
FROM MY_VIEW
WHERE 1=1
AND "DATE" > ADD_MONTHS(TO_DATE(SYSDATE, 'DD-MM-RR'), -1)
AND ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04')
)
PIVOT
(
MAX(TOTAL) FOR ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04', 'ROW_TOTAL')
)
ORDER BY 1
And the end results should look like this:
DATE ITEM-01 ITEM-02 ITEM-03 ITEM-04 ROW_TOTAL
======================================================
4/18/17 1,063,008 460,436 106,715 97,532 1,829,364
4/19/17 1,061,819 479,338 103,946 108,179 1,859,825
4/20/17 1,095,853 536,835 107,437 101,949 1,944,677
4/21/17 1,153,345 642,364 108,940 106,988 2,121,068
4/22/17 1,075,849 633,873 102,459 99,999 2,012,710
4/23/17 913,952 591,783 95,291 100,144 1,794,358
4/24/17 1,036,377 626,043 115,105 98,339 1,977,043
4/25/17 1,079,163 602,237 118,189 100,478 2,001,529
4/26/17 1,110,499 639,640 109,793 103,360 2,069,311
4/27/17 1,119,696 620,081 105,781 108,276 2,061,452
4/28/17 1,125,676 618,763 113,234 96,326 2,057,169
4/29/17 1,026,974 620,059 102,856 96,150 1,940,394
4/30/17 903,913 539,694 83,531 97,073 1,716,114
5/1/17 1,043,598 590,027 100,272 96,519 1,932,843
5/2/17 1,074,912 623,392 101,793 97,724 2,000,981
5/3/17 1,078,865 620,662 101,699 102,900 2,010,014
5/4/17 1,090,501 628,785 110,248 103,593 2,040,658
5/5/17 1,125,984 686,945 128,657 105,356 2,150,037
5/6/17 1,031,267 625,189 117,290 99,358 1,967,819
5/7/17 921,467 551,497 97,482 93,520 1,752,940
5/8/17 1,064,291 624,366 93,463 98,860 1,979,863
5/9/17 1,085,062 661,509 97,791 98,083 2,039,114
5/10/17 1,103,794 634,868 94,364 102,345 2,033,911
5/11/17 1,107,449 617,931 94,420 103,717 2,024,126
5/12/17 1,130,463 647,744 97,616 102,684 2,079,009
5/13/17 1,056,653 621,182 96,743 99,801 1,974,710
5/14/17 970,969 583,865 87,953 97,682 1,831,516
5/15/17 1,075,979 633,102 95,356 101,336 2,003,830
5/16/17 1,094,805 634,421 96,802 99,533 2,026,891
GRAND TOTAL 30,822,183 17,596,631 2,985,226 2,917,804 57,233,276
It might go faster if you use 'analytical queries' to perform totalling without needing to run separate grouping queries. An example analytic expression might be:
Select
Sum(item_count) over(partition by date) --btw "date" is a poor name choice for a column
From
Table
Where
Item_name in ...
Or alternatively, use 'grouping sets', 'cube' or 'rollup'
The difference? Analytics establish grouping characteristics that add an extra column to a report with aggregation of the row. Grouping sets, cubes and roll ups add extra rows to a report with aggregations of a column
Apologies for not giving an example of this; they're quite an extensive topic requiring in depth discussion so it's partly beyond the scope of my answer, and partly that I'm writing this on an iPad with no recent use of them to call on from memory (the topic is that vast) and no way to test or run one, so I'll leave it as a pointer for you to do further background research. Essentially a grouping set is an instruction akin to "here's a single data set, iterate it once and perform these N number of different group by aggregates as you go.." essentially one group would be by date and name (so single lines are output) and the other group by is probably by name (so totals for each name are output)..
then do your pivot. For more info, the 'phrases in quotes' are what you'd look up in the manual/web
All this is a little bit dirty, by the way.. your reporting tool from end should really be building this summary, rather than oracle, though doing grouping (but not pivoting) in the DB helpfully reduces network traffic

SSRS limit length of text but keep full words

In my table I have essentially 2 columns (many more but there is an obvious left side and right side). One of the fields, FIELD1, on the left side comes from a LookupSet() and each ID can have two items from FIELD1. Using a join(lookupset(), vbcrlf) I am able to get both values for the ID and put it into one cell in the table. This works but with the vbcrlf, the row height is increased. This causes a problem because there is data on the right side which cannot have additional space between it.
I did a split(join(lookupset())).getValue(0) for the first row and then the row below it for value 1. With some iif statements to check errors etc, this works.
One problem I solved is that the values for FIELD1 can be longer than the width of the cell, but will not take up more than two. I was able to do some substring magic in oracle like:
SELECT ID, SUBSTR(FIELD1, 1, 70) FROM....
UNION
SELECT ID, SUBSTR(FIELD1, 70) FROM ...
using sorting, I am able to get up to 4 rows of data per ID which I will be able to split the lookupset and get each value into the table.
My last problem, and hopefully someone can help with is that when I ge the substring, It can cut off words and the next row will start with the rest of the word.
Is there a way, possibly using a regex to make sure to keep words in tact, but also to ensure that the total length returned is < some number of characters? I am happy to abandon any part of the approach i am currently taking if I am off track.
I was able to solve this (with some help) by using
SUBSTR(FIELD1,1, regexp_instr(FIELD1, '[ ]', 70))
SELECT ID, SUBSTR(FIELD1,1, regexp_instr(FIELD1, '[ ]', 70)) FROM....
UNION
SELECT ID, SUBSTR(FIELD1, regexp_instr(FIELD1, '[ ]', 70)) FROM ...

How to? Correct sql syntax for finding the next available identifier

I think I could use some help here from more experienced users...
I have an integer field name in a table, let's call it SO_ID in a table SO, and to each new row I need to calculate a new SO_ID based on the following rules
1) SO_ID consists of 6 letters where first 3 are an area code, and the last three is the sequenced number within this area.
309001
309002
309003
2) so the next new row will have a SO_ID of value
309004
3) if someone deletes the row with SO_ID value = 309002, then the next new row must recycle this value, so the next new row has got to have the SO_ID of value
309002
can anyone please provide me with either a SQL function or PL/SQL (perhaps a trigger straightaway?) function that would return the next available SO_ID I need to use ?
I reckon I could get use of keyword rownum in my sql, but the follwoing just doens't work properly
select max(so_id),max(rownum) from(
select (so_id),rownum,cast(substr(cast(so_id as varchar(6)),4,3) as int) from SO
where length(so_id)=6
and substr(cast(so_id as varchar(6)),1,3)='309'
and cast(substr(cast(so_id as varchar(6)),4,3) as int)=rownum
order by so_id
);
thank you for all your help!
This kind of logic is fraught with peril. What if two sessions calculate the same "next" value, or both try to reuse the same "deleted" value? Since your column is an integer, you'd probably be better off querying "between 309001 and 309999", but that begs the question of what happens when you hit the thousandth item in area 309?
Is it possible to make SO_ID a foreign key to another table as well as a unique key? You could pre-populate the parent table with all valid IDs (or use a function to generate them as needed), and then it would be a simple matter to select the lowest one where a child record doesn't exist.
well, we came up with this... sort of works.. concurrency is 'solved' via unique constraint
select min(lastnumber)
from
(
select so_id,so_id-LAG(so_id, 1, so_id) OVER (ORDER BY so_id) AS diff,LAG(so_id, 1, so_id) OVER (ORDER BY so_id)as lastnumber
from so_miso
where substr(cast(so_id as varchar(6)),1,3)='309'
and length(so_id)=6
order by so_id
)a
where diff>1;
Do you really need to compute & store this value at the time a row is inserted? You would normally be better off storing the area code and a date in a table and computing the SO_ID in a view, i.e.
SELECT area_code ||
LPAD( DENSE_RANK() OVER( PARTITION BY area_code
ORDER BY date_column ),
3,
'0' ) AS so_id,
<<other columns>>
FROM your_table
or having a process that runs periodically (nightly, for example) to assign the SO_ID using similar logic.
If your application is not pure sql, you could do this in application code (ie: Java code). This would be more straightforward.
If you are recycling numbers when rows are deleted, your base table must be consulted when generating the next number. "Legacy" pre-relational schemes that attempt to encode information in numbers are a pain to make airtight when numbers must be recycled after deletes, as you say yours must.
If you want to avoid having to scan your table looking for gaps, an after-delete routine must write the deleted number to a separate table in a "ReuseMe" column. The insert routine does this:
begins trans
selects next-number table for update
uses a reuseme number if available else uses the next number
clears the reuseme number if applicable or increments the next-number in the next-number table
commits trans
Ignoring the issues about concurrency, the following should give a decent start.
If 'traffic' on the table is low enough, go with locking the table in exclusive mode for the duration of the transaction.
create table blah (soc_id number(6));
insert into blah select 309000 + rownum from user_tables;
delete from blah where soc_id = 309003;
commit;
create or replace function get_next (i_soc in number) return number is
v_min number := i_soc* 1000;
v_max number := v_min + 999;
begin
lock table blah in exclusive mode;
select min(rn) into v_min
from
(select rownum rn from dual connect by level <= 999
minus
select to_number(substr(soc_id,4))
from blah
where soc_id between v_min and v_max);
return v_min;
end;

Resources