how to create parentchild hierachery in ssrs single parameter - ssrs-2008

I have one doubt in ssrs .
how to create parent hierachery in single parameter in ssrs with table report using sql server database table.
table: empdetails:
CREATE TABLE [dbo].[empdetails](
[empid] [int] NULL,
[country] [varchar](50) NULL,
[state] [varchar](50) NULL,
[empname] [varchar](50) NULL,
[deptno] [int] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[empdetails] ([empid], [country], [state], [empname], [deptno]) VALUES (1, N'india', N'ap', N'abc', 10)
GO
INSERT [dbo].[empdetails] ([empid], [country], [state], [empname], [deptno]) VALUES (2, N'india', N'ka', N'def', 20)
GO
INSERT [dbo].[empdetails] ([empid], [country], [state], [empname], [deptno]) VALUES (3, N'india', N'tn', N'de', 30)
GO
INSERT [dbo].[empdetails] ([empid], [country], [state], [empname], [deptno]) VALUES (4, N'usa', N'TX', N'deet', 50)
GO
INSERT [dbo].[empdetails] ([empid], [country], [state], [empname], [deptno]) VALUES (5, N'usa', N'NJ', N'ion', 60)
GO
INSERT [dbo].[empdetails] ([empid], [country], [state], [empname], [deptno]) VALUES (6, N'usa', N'WV', N'xy', 70)
GO
INSERT [dbo].[empdetails] ([empid], [country], [state], [empname], [deptno]) VALUES (7, N'uk', N'Belfast', N'io', 40)
GO
INSERT [dbo].[empdetails] ([empid], [country], [state], [empname], [deptno]) VALUES (8, N'uk', N'Wycombe', N'un', 50)
GO
based on above table : I want create parent hierrachery in singe paramerte like below in ssrs table report :
country....>State: drop down look like below:
paramername:countryandstateparamer:
india
ap
ka
tn
usa
nj
wv
tx
uk
Belfast
Wycombe
here If I select india then dispaly corresponding states data (ap,ka,tn).
if I select ap state then display only ap related data(ap)
if I select ap and ka state then display only ap and ka data( ap and ka)
similay to other counry and states.
I tried like below :
dataset : select * from empdetails where country in #country and state in ( #state)
dataset 1: select distinct country from empdetails
dataset2 : select distinct state from empdetails where county=#country
after that I go throught two paramerts mapping
here preview time I got two paramater one is country paramaterand 2nd is state paramater.But I want only creat only one paramater that parater show like counryandstate hierchar using sql server table data.
please tell me how to solve this issue in ssrs report multiple hieracher in single paramater .

There is no built-in way to build a hierarchy with SQL Server as the source (SSAS supports this though).
We can do with with a bit of code, the first part could be done using a view but this should work even though it seems like a lot of code, most of it is repeated (hence why a view would be better)
Anyway,
First we need a dataset to supply data to the parameter list.
First dataset
Create a dataset called dsRegions
Set the dataset query to be
/* This could be built as a view to simplfy things */
-- create a table for the results
DECLARE #regions TABLE(RegionDisplayName varchar(50), RegionType varchar(1), RegionOrder decimal (10,3), RegionName varchar(50))
-- first get the countries in order and assign a sort order
INSERT INTO #regions
SELECT *
, ROW_NUMBER() OVER(ORDER BY country) AS CountryOrder
, country
FROM
(SELECT DISTINCT Country, 'C' AS RegionType FROM empdetails) e
-- now get the states and assign the sort order from the country plus a sort oder for the state (supports 999 states/country)
INSERT INTO #regions
SELECT
' ' + state, 'S' -- indent the states so they look better in the parameter list
, RegionOrder + CAST(ROW_NUMBER() OVER(PARTITION BY c.RegionName ORDER BY state) as decimal(10,3)) / 1000 AS StateOrder
, state
FROM #regions c
JOIN empdetails e on c.RegionName = e.country
-- finally output the results
SELECT * FROM #regions order by regionorder
Parameter : let's call this #pRegion
So create a parameter called pRegion and set its available values dataset to dsRegions.
important Set the Value to RegionName and the label to RegionDisplayName
This will give us a nice ordered indented list for the user to see.
Main dataset query
The main dataset query repeats most of the parameter dataset query
The only difference is the final output which filters both country and state and if either match it will return the records. The query is as follows
-- create a table for the results
DECLARE #regions TABLE(RegionDisplayName varchar(50), RegionType varchar(1), RegionOrder decimal (10,3), RegionName varchar(50))
-- first get the countries in order and assign a sort order
INSERT INTO #regions
SELECT *
, ROW_NUMBER() OVER(ORDER BY country) AS CountryOrder
, country
FROM
(SELECT DISTINCT Country, 'C' AS RegionType FROM empdetails) e
-- now get the states and assign the sort order from the country plus a sort oder for the state (supports 999 states/country)
INSERT INTO #regions
SELECT
' ' + state, 'S' -- indent the states so they look better in the parameter list
, RegionOrder + CAST(ROW_NUMBER() OVER(PARTITION BY c.RegionName ORDER BY state) as decimal(10,3)) / 1000 AS StateOrder
, state
FROM #regions c
JOIN empdetails e on c.RegionName = e.country
-- output
SELECT
e.*
FROM empdetails e
WHERE country in (#pRegion)
or state in (#pRegion)
I've done a simple test and this works as expected, so if you select UK and TX you will get records for Belfast, Wycombe and TX
Hope this helps.

Related

Set repeating IDs till first record repeats (bulk load csv file)

I have a file that I imported via bulk-insert and I want to assign group IDs/sequences.
I would like to assign the IDs till the first record with the first character is repeated. In this example its "A".
The challenge I have is how to achieve this example and set the IDs like this example:
ID
data
1
A000abcefd
1
E00asoaskdaok
1
C000dasdasok
2
A100abcasds
2
E100aandas
2
C100adsokdas
Here is one way to do it, but given the limited info you provided I will make the following assumptions:
**The data in your table has some order to it. This obviously will not work if that is not the case. I used an ID, you use what you have.
**The first row in the table has the character you are looking for.
CREATE TABLE #tmp(ID int, [data] varchar(20))
INSERT INTO #tmp
VALUES
(1, 'A000abcefd'),
(2, 'E00asoaskdaok'),
(3, 'C000dasdasok'),
(4, 'A100abcasds'),
(5, 'E100aandas'),
(6, 'C100adsokdas')
DECLARE #CHAR varchar(1)
SELECT #CHAR = (SELECT TOP 1 SUBSTRING([data],1,1) FROM #tmp ORDER BY ID)
SELECT SUM(CASE WHEN SUBSTRING([data],1,1) = #CHAR THEN 1 ELSE 0 END)
OVER(ORDER BY ID ROWS BETWEEN UNBOUNDED PRECEDING and CURRENT ROW) SeqNum
,[data]
FROM #tmp

TSQL Get Item Price History from Item Price Changes

I have a table of item price changes, and I want to use it to create a table of item prices for each date (between the item's launch and end dates).
Here's some code to create the date:-
declare #Item table (item_id int, item_launch_date date, item_end_date date);
insert into #Item Values (1,'2001-01-01','2016-01-01'), (2,'2001-01-01','2016-01-01')
declare #ItemPriceChanges table (item_id int, item_price money, my_date date);
INSERT INTO #ItemPriceChanges VALUES (1, 123.45, '2001-01-01'), (1, 345.34, '2001-01-03'), (2, 34.34, '2001-01-01'), (2,23.56 , '2005-01-01'), (2, 56.45, '2016-05-01'), (2, 45.45, '2017-05-01'); ;
What I'd like to see is something like this:-
item_id date price
------- ---- -----
1 2001-01-01 123.45
1 2001-01-02 123.45
1 2001-01-03 345.34
1 2001-01-04 345.34
etc.
2 2001-01-01 34.34
2 2001-01-02 34.34
etc.
Any suggestions on how to write the query?
I'm using SQL Server 2016.
Added:
I also have a calendar table called "dim_calendar" with one row per day. I had hoped to use a windowing function, but the nearest I can find is lead() and it doesn't do what I thought it would do:-
select
i.item_id,
c.day_date,
ipc.item_price as item_price_change,
lead(item_price,1,NULL) over (partition by i.item_id ORDER BY c.day_date) as item_price
from dim_calendar c
inner join #Item i
on c.day_date between i.item_launch_date and i.item_end_date
left join #ItemPriceChanges ipc
on i.item_id=ipc.item_id
and ipc.my_date=c.day_date
order by
i.item_id,
c.day_date;
Thanks
I wrote this prior to your edit. Note that your sample output suggests that an item can have two prices on the day of the price change. The following assumes that an item can only have one price on a price change day and that is the new price.
declare #Item table (item_id int, item_launch_date date, item_end_date date);
insert into #Item Values (1,'2001-01-01','2016-01-01'), (2,'2001-01-01','2016-01-01')
declare #ItemPriceChange table (item_id int, item_price money, my_date date);
INSERT INTO #ItemPriceChange VALUES (1, 123.45, '2001-01-01'), (1, 345.34, '2001-01-03'), (2, 34.34, '2001-01-01'), (2,23.56 , '2005-01-01'), (2, 56.45, '2016-05-01'), (2, 45.45, '2017-05-01');
SELECT * FROM #ItemPriceChange
-- We need a table variable holding all possible date points for the output
DECLARE #DatePointList table (DatePoint date);
DECLARE #StartDatePoint date = '01-Jan-2001';
DECLARE #MaxDatePoint date = GETDATE();
DECLARE #DatePoint date = #StartDatePoint;
WHILE #DatePoint <= #MaxDatePoint BEGIN
INSERT INTO #DatePointList (DatePoint)
SELECT #DatePoint;
SET #DatePoint = DATEADD(DAY,1,#DatePoint);
END;
-- We can use a CTE to sequence the price changes
WITH ItemPriceChange AS (
SELECT item_id, item_price, my_date, ROW_NUMBER () OVER (PARTITION BY Item_id ORDER BY my_date ASC) AS SeqNo
FROM #ItemPriceChange
)
-- With the price changes sequenced, we can derive from and to dates for each price and use a join to the table of date points to produce the output. Also, use an inner join back to #item to only return rows for dates that are within the start/end date of the item
SELECT ItemPriceDate.item_id, DatePointList.DatePoint, ItemPriceDate.item_price
FROM #DatePointList AS DatePointList
INNER JOIN (
SELECT ItemPriceChange.item_id, ItemPriceChange.item_price, ItemPriceChange.my_date AS from_date, ISNULL(ItemPriceChange_Next.my_date,#MaxDatePoint) AS to_date
FROM ItemPriceChange
LEFT OUTER JOIN ItemPriceChange AS ItemPriceChange_Next ON ItemPriceChange_Next.item_id = ItemPriceChange.item_id AND ItemPriceChange.SeqNo = ItemPriceChange_Next.SeqNo - 1
) AS ItemPriceDate ON DatePointList.DatePoint >= ItemPriceDate.from_date AND DatePointList.DatePoint < ItemPriceDate.to_date
INNER JOIN #item AS item ON item.item_id = ItemPriceDate.item_id AND DatePointList.DatePoint BETWEEN item.item_launch_date AND item.item_end_date
ORDER BY ItemPriceDate.item_id, DatePointList.DatePoint;
#AlphaStarOne Perfect! I've modified it to use a Windowing function rather than a self-join, but what you've suggested works. Here's my implementation of that in case anyone else needs it:
SELECT
ipd.item_id,
dc.day_date,
ipd.item_price
FROM dim_calendar dc
INNER JOIN (
SELECT
item_id,
item_price,
my_date AS from_date,
isnull(lead(my_date,1,NULL) over (partition by item_id ORDER BY my_date),getdate()) as to_date
FROM #ItemPriceChange ipc1
) AS ipd
ON dc.day_date >= ipd.from_date
AND dc.day_date < ipd.to_date
INNER JOIN #item AS i
ON i.item_id = ipd.item_id
AND dc.day_date BETWEEN i.item_launch_date AND i.item_end_date
ORDER BY
ipd.item_id,
dc.day_date;

How to filter a query based on a jsonb data?

Not even sure if it's possible to do this kind of query in postgres. At least i'm stuck.
I have two tables: a product recommendation list, containing multiple products to be recommended to a particular customer; and a transaction table indicating the product bought by customer and transaction details.
I'm trying to track the performance of my recommendation by plotting all the transaction that match the recommendations (both customer and product).
Below is my test case.
Kindly help
create table if not exists productRec( --Product Recommendation list
task_id int,
customer_id int,
detail jsonb);
truncate productRec;
insert into productRec values (1, 2, '{"1":{"score":5, "name":"KitKat"},
"4":{"score":2, "name":"Yuppi"}
}'),
(1, 3, '{"1":{"score":3, "name":"Yuppi"},
"4":{"score":2, "name":"GoldenSnack"}
}'),
(1, 4, '{"1":{"score":3, "name":"Chickies"},
"4":{"score":2, "name":"Kitkat"}
}');
drop table txn;
create table if not exists txn( --Transaction table
customer_id int,
item_id text,
txn_value numeric,
txn_date date);
truncate txn;
insert into txn values (1, 'Yuppi', 500, DATE '2001-01-01'), (2, 'Kitkat', 2000, DATE '2001-01-01'),
(3, 'Kitkat', 2000, DATE '2001-02-01'), (4, 'Chickies', 200, DATE '2001-09-01');
--> Query must plot:
--Transaction value vs date where the item_id is inside the recommendation for that customer
--ex: (2000, 2001-01-01), (200, 2001-09-01)
We can get each recommendation as its own row with jsonb_each. I don't know what to do with the keys so I just take the value (still jsonb) and then the name inside it (the ->> outputs text).
select
customer_id,
(jsonb_each(detail)).value->>'name' as name
from productrec
So now we have a list of customer_ids and item_ids they were recommended. Now we can just join this with the transactions.
select
txn.txn_value,
txn.txn_date
from txn
join (
select
customer_id,
(jsonb_each(detail)).value->>'name' as name
from productrec
) p ON (
txn.customer_id = p.customer_id AND
lower(txn.item_id) = lower(p.name)
);
In your example data you spelled Kitkat differently in the recommendation table for customer 2. I added lowercasing in the join condition to counter that but it might not be the right solution.
txn_value | txn_date
-----------+------------
2000 | 2001-01-01
200 | 2001-09-01
(2 rows)

Concatenated columns should not match in 2 tables

I'll just put this in layman's terms since I'm a complete noobie:
I have 2 tables A and B, both having 2 columns of interest namely: employee_number and salary.
What I am looking to do is to extract rows of 'combination' of employee_number and salary from A that are NOT present in B, but each of employee_number and salary should be present in both.
I am looking to doing it with the 2 following conditions(please forgive the wrong function
names.. this is just to present the problem 'eloquently'):
1.) A.unique(employee_number) exists in B.unique(employee_number) AND A.unique(salary)
exists in B.unique(salary)
2.) A.concat(employee_number,salary) <> B.concat(employee_number,salary)
Note: A and B are in different databases, so I'm looking to use dblink to do this.
This is what I tried doing:
SELECT distinct * FROM dblink('dbname=test1 port=5432
host=test01 user=user password=password','SELECT employee_number,salary, employee_number||salary AS ENS FROM empsal.A')
AS A(employee_number int8, salary integer, ENS numeric)
LEFT JOIN empsalfull.B B on B.employee_number = A.employee_number AND B.salary = A.salary
WHERE A.ENS not in (select distinct employee_number || salary from empsalfull.B)
but it turned out to be wrong as I had it cross-checked by using spreadsheets and I don't get the same result.
Any help would be greatly appreciated. Thanks.
For easier understanding I left out the dblink.
Because, the first one selects lines in B that equal the employeenumber in A as well as the salery in A, so their concatenated values will equal as well (if you expect this to not be true, please provide some test data).
SELECT * from firsttable A
LEFT JOIN secondtable B where
(A.employee_number = B.employee_number AND a.salery != b.salery) OR
(A.salery = B.salery AND A.employee_number != B.employee_number)
If you have troubles with lines containing nulls, you might also try somthing like this:
AND (a.salery != b.salery OR (a.salery IS NULL AND b.salery IS NOT NULL) or (a.salery IS NOT
NULL and b.salery IS NULL))
I think you're looking for something along these lines.
(Sample data)
create table A (
employee_number integer primary key,
salary integer not null
);
create table B (
employee_number integer primary key,
salary integer not null
);
insert into A values
(1, 20000),
(2, 30000),
(3, 20000); -- This row isn't in B
insert into B values
(1, 20000), -- Combination in A
(2, 20000), -- Individual values in A
(3, 50000); -- Only emp number in A
select A.employee_number, A.salary
from A
where (A.employee_number, A.salary) NOT IN (select employee_number, salary from B)
and A.employee_number IN (select employee_number from B)
and A.salary IN (select salary from B)
output: 3, 20000

Find duplicate row "details" in table

OrderId OrderCode Description
-------------------------------
1 Z123 Stuff
2 ABC999 Things
3 Z123 Stuff
I have duplicates in a table like the above. I'm trying to get a report of which Orders are duplicates, and what Order they are duplicates of, so I can figure out how they got into the database.
So ideally I'd like to get an output something like;
OrderId IsDuplicatedBy
-------------------------
1 3
3 1
I can't work out how to code this in SQL.
You can use the same table twice in one query and join on the fields you need to check against. T1.OrderID <> T2.OrderID is needed to not find a duplicate for the same row.
declare #T table (OrderID int, OrderCode varchar(10), Description varchar(50))
insert into #T values
(1, 'Z123', 'Stuff'),
(2, 'ABC999', 'Things'),
(3, 'Z123', 'Stuff')
select
T1.OrderID,
T2.OrderID as IsDuplicatedBy
from #T as T1
inner join #T as T2
on T1.OrderCode = T2.OrderCode and
T1.Description = T2.Description and
T1.OrderID <> T2.OrderID
Result:
OrderID IsDuplicatedBy
1 3
3 1