Change row value qlikview - qlikview

IF(LEVEL2 = '16.30' AND (len(LEVEL3 > '0')), LEVEL2, LEVEL1) AS LEVEL4
When I load this line, it will output in a pivot table showing 16.3 because in my excel sheet that I load from, 16.30 is shown as 16.3 (For I don't know what reason. My company say it's like that).
What I would like to do is to change the 16.3 showing in the pivot table to '16.30'. Is there any way I can do it like for example
IF(LEVEL2 = '16.30' AND (len(LEVEL3 > '0')), LEVEL2 AS '16.30', LEVEL1) AS LEVEL4
The above code does not work btw. Thank very much.

This line does not make sense
IF(LEVEL2 = '16.30' AND (len(LEVEL3 > '0')), LEVEL2 AS '16.30', LEVEL1) AS LEVEL4
Change to
IF(LEVEL2 = '16.30' AND len(LEVEL3) > '0', LEVEL2 AS '16.30', LEVEL1) AS LEVEL4
And to get the correct value formatted properly you would use
Num(IF(LEVEL2 = '16.30' AND len(LEVEL3) > '0', LEVEL2 AS '16.30', LEVEL1),'# ##0.00') AS LEVEL4

Related

How to use LIKE with ORDER BY CASE for partial text search in WHEN statement?

How do you use LIKE with ORDER BY CASE in SQL? Or in other words, how do you do a partial text search for the following:
ORDER BY CASE [Column Name] WHEN [value partial text%]
Problem: I'm referencing a table (named "Personnel") with column (titled "Rank"), which lists each employee's job title followed by their level of certification (many variables). I would like to order the SQL query results by job title, ignoring the certification level that follows title name.
Example values in Personnel.Rank Column:
Captain Paramedic
Captain Intermediate
Captain EMT
Lieutenant Paramedic
Lieutenant Intermediate
Lieutenant EMT
Apparatus Operator Paramedic
Firefighter EMT
Firefighter AEMT
This works, but I don't want to list every variable as a WHEN clause:
SELECT
p.Rank
FROM Personnel p
ORDER BY
CASE p.Rank
WHEN 'Captain Paramedic' THEN 1
WHEN 'Captain EMT' THEN 1
WHEN 'Lieutenant Paramedic' THEN 2
WHEN 'Lieutenant EMT' THEN 2
ELSE 3
END
I would like to know how to do something like this instead:
SELECT
p.Rank
FROM Personnel p
ORDER BY
CASE p.Rank
WHEN LIKE 'Captain%' THEN 1
WHEN LIKE 'Lieutenant%' THEN 2
ELSE 3
END
Thoughts?
LIKE operator is not permitted with ORDER BY CASE [column name] WHEN statement
It is possible you can just fix up the syntax and still use your case statement. Untested but the syntax I would expect is:
SELECT
p.Rank
FROM Personnel p
ORDER BY
CASE
WHEN p.Rank LIKE 'Captain%' THEN 1
WHEN p.Rank LIKE 'Lieutenant%' THEN 2
ELSE 3
END
A more general purpose solution would be to use a reference or lookup table to get your order by values. Here is an example written in PSEUDOSQL just to show the idea. In real life, you can create your table or use a temp table or a CTE. The pro here is you can maintain this a little more cleanly (in your ref table - or SortOrder can be a field right in your Personnel table). The con here is you have "promoted" a simple ordering problem into something more permanent and if this is only an ad hoc need for an ad hoc query then it might be overkill.
create temporary table SortOrder (Rank, SortOrder)
insert into SortOrder
values
('Captain Paramedic', 10),
('Captain Intermediate', 10),
('Captain EMT', 10),
('Lieutenant Paramedic', 20),
('Lieutenant Intermediate', 20),
('Lieutenant EMT', 20),
('Apparatus Operator Paramedic', 30),
('Firefighter EMT', 40),
('Firefighter AEMT', 40)
SELECT
p.Rank
FROM
Personnel p
LEFT JOIN SortOrder s
ON p.Rank = s.Rank
ORDER BY
COALESCE(s.SortOrder, 100)

T-SQL: Single Table contains Permissions Tree

I have a SQL Server Table that contains a 'Hierarchy/Tree' of User Permissions.
Each Individual Permission can have values: 1 [Allowed], Blank [Not Allowed] & 0 [specifically Cancelled].
Each Individual Permission can be in one or more 'Permission Groups' & a User can be assigned all the Individual Permissions in one or more Permission Groups.
Each of the 'Permission Groups', in turn, can be in one or more higher level permission groups ... and eventually, all Permissions Groups are under a Master Group named 'Main Menu'.
This SQL Code:
Select
'Main Menu' Base,
Description Level1,
ParentId,
SecurityNodesId,
ListOrder,
Category,
LastModified
From SecurityNodes
Where ParentId = 1
Order By Description
Produces the following Output:
'Main Menu' has a ParentId of NULL [Not Shown in screenshot].
The 'Level1' 'Folders' contain other folders or Individual Permissions which are 'Referenced' by the Values under SecurityNodesId.
For instance, a search for SecurityNodesId 102 [Level1 - Administration] in the ParentId column returns this list of Sub Folders under 'Level2':
So ... I can access each of these sub folders by writing separate queries.
But what I want is to have an end result that displays every Node of this Permissions Tree in Table form like this:
Main Menu Level1 Level2 Level3 Level4 PermissionName PermissionValue
I have never had to do something this complex before, though I have done plenty of self-joins.
I am currently thinking that I would need to do a self join to each self join ... to get to successive Levels of the Tree ... but I believe there may be a 'recursive' approach to this that might be more efficient?
I would appreciate any help I can get with this.
Thanks in advance!
The way to solve this is with a Recursive CTE.
These are definitely more advanced than your usual SQL, but once you have your head wrapped around them, they are pretty easy to put together and VERY useful for hierarchical data (any table that stores a parent/child relationship).
A recursive CTE has two parts, separated by a UNION ALL.
The recursive seed which is ran only once and determines the starting result set for the recursion. For you, this is likely any record with a parentId of 1.
The recursive term (or member) which joins the cte (itself) to the table that holds the parent/child relationship. It will run over and over and over and over again until the Join or a WHERE filter causes it to return no new records.
In your case, it will look something like below. Note that I don't know what your starting table looks like. Namely the Level1 column from your original SQL isn't clear if that is the column name or an alias you call Level1. Furthermore it's not at all clear how you derive a "Permission Group" or "Permission Value" from this data. But... at any rate this should get you in the ballpark:
WITH reccte as (
/*
* To start the recursion we need a "Seed"... or a set of data
* that defines the starting point on which we iterate after
* the UNION ALL below.
*
* The seed here is all records with a parentid of 1
*/
SELECT Base,
ParentID,
SecurityNodesID,
Level as Level1,
NULL as Level2,
NULL as Level3,
NULL as Level4,
'?' as PermissionName,
Category as PermissionValue,
1 as depth, --track how deep we recurse
Base + '>' + Level as path --keep track of where we've been and what has led us to this point in recurssion
FROM SecurityNodes
UNION ALL
/*
* This section is the part that iterates. It continues to join
* all rows that have been collected up that point with the Security
* Nodes table until that join fails.
*/
SELECT
reccte.Base,
SecurityNodes.ParentID,
SecurityNodes.SecurityNodesID,
reccte.Level1,
/*
* Depending on how deep we are in the security hierarchy
* capture the level string to the appropriate column
*/
CASE WHEN depth = 1 THEN SecurityNodes.Level ELSE reccte.Level2,
CASE WHEN depth = 2 THEN SecurityNodes.Level ELSE reccte.Level3,
CASE WHEN depth = 3 THEN SecurityNodes.Level ELSE reccte.Level4,
'?' as PermissionName,
SecurityNodes.Category as PermissionValue,
reccte.depth + 1, --increment depth
reccte.path + '>' + SecurityNodes.Level --add to the path so we know how we got here
FROM reccte
INNER JOIN SecurityNodes
/*Join parent to child*/
ON reccte.SecurityNodesId = SecurityNodes.parentId
WHERE depth < 5 --Stop looking up if we go deeper than 4 levels.
)
SELECT *
FROM reccte
While we track depth here and stop the recursion if we hit a depth of 4, you could stop the recursion with the MAXRECURSIVE option/hint. That would just go at the end of your query:
SELECT *
FROM reccte
OPTION (MAXRECURSION 4);
It's important to add either/or to your recursive CTE otherwise you risk causing an infinite loop should a security node have a child that is also one of its ancestors which would cause it to cycle endlessly.
OPTION (MAXRECURSION 2);
I followed through on an idea I mentioned in my original post and it looks like I have achieved what I was wanting.
I don't think it is the best possible solution because I know how many total levels there currently are. If we suddenly add another level or two, the SQL will not capture everything and I'll manually have to add one or more Left Joins.
Select
'Main Menu' Base,
sn.Description Level1,
sn2.Description Level2,
sn3.Description Level3,
sn4.Description Level4,
sn.ParentId,
sn.SecurityNodesId,
sn.ListOrder,
sn.Category,
sn.LastModified
From
SecurityNodes sn
Left Join SecurityNodes sn2 On sn2.ParentId = sn.SecurityNodesId
Left Join SecurityNodes sn3 On sn3.ParentId = sn2.SecurityNodesId
Left Join SecurityNodes sn4 On sn3.ParentId = sn3.SecurityNodesId
Order By sn.ParentId, sn.Description
I would still appreciate any suggestions for a more elegant/dynamic way of achieving what I need ... but for now, the above SQL is doing the job.

How to correctly order a recursive DB2 query

Hello friendly internet wizards.
I am attempting to extract a levelled bill of materials (BOM) from a dataset, running in DB2 on an AS400 server.
I have constructed most of the query (with a lot of help from online resources), and this is what I have so far;
#set item = '10984'
WITH BOM (origin, PMPRNO, PMMTNO, BOM_Level, BOM_Path, IsCycle, IsLeaf) AS
(SELECT CONNECT_BY_ROOT PMPRNO AS origin, PMPRNO, PMMTNO,
LEVEL AS BOM_Level,
SYS_CONNECT_BY_PATH(TRIM(PMMTNO), ' : ') BOM_Path,
CONNECT_BY_ISCYCLE IsCycle,
CONNECT_BY_ISLEAF IsLeaf
FROM MPDMAT
WHERE PMCONO = 405 AND PMFACI = 'M01' AND PMSTRT = 'STD'
START WITH PMPRNO = :item
CONNECT BY NOCYCLE PRIOR PMMTNO = PMPRNO)
SELECT 0 AS BOM_Level, '' AS BOM_Path, MMITNO AS Part_Number, MMITDS AS Part_Name,
IFSUNO AS Supplier_Number, IDSUNM AS Supplier_Name, IFSITE AS Supplier_Part_Number
FROM MITMAS
LEFT OUTER JOIN MITVEN ON MMCONO = IFCONO AND MMITNO = IFITNO AND IFSUNO <> 'ZGA'
LEFT OUTER JOIN CIDMAS ON MMCONO = IDCONO AND IDSUNO = IFSUNO
WHERE MMCONO = 405
AND MMITNO = :item
UNION ALL
SELECT BOM.BOM_Level, BOM_Path, BOM.PMMTNO AS Part_Number, MMITDS AS Part_Name,
IFSUNO AS Supplier_Number, IDSUNM AS Supplier_Name, IFSITE AS Supplier_Part_Number
FROM BOM
LEFT OUTER JOIN MITMAS ON MMCONO = 405 AND MMITNO = BOM.PMMTNO
LEFT OUTER JOIN MITVEN ON IFCONO = MMCONO AND IFITNO = MMITNO AND IFSUNO <> 'ZGA' AND MMMABU = '2'
LEFT OUTER JOIN CIDMAS ON MMCONO = IDCONO AND IDSUNO = IFSUNO
;
This is correctly extracting the components for a given item, as well as the sub-components (etc).
Current data looks like this (I have stripped out some columns that aren't relevant to the issue);
https://pastebin.com/LUnGKRqH
My issue is the order that the data is being presented in.
As you can see in the pastebin above, the first column is the 'level' of the component. This starts with the parent item at level 0, and can theoretically go down as far as 99 levels.
The path is also show there, so for example the second component 853021 tells us that it's a 2nd level component, the paths up to INST363 (shown later in the list as a 1st level), then up to the parent at level 0.
I would like for the output to show in path order (for lack of a better term).
Therefore, after level 0, it should be showing the first level 1 component, and then immediately be going into it's level 2 components and so on, until no further level is found. Then at that point, it returns back up the path to the next valid record.
I hope I have explained that adequately, but essentially the data should come out as;
Level
Path
Item
0
10984
1
: INST363
INST363
2
: INST363 : 853021
853021
1
: 21907
21907
Any help that can be provided would be very much appreciated!
Thanks,
This is an interesting query. Frankly I am surprised it works as well as it does since it is not structured the way I usually structure queries with a recursive CTE. The main issue is that while you have the Union in there, it does not appear to be within the CTE portion of the query.
When I write a recursive CTE, it is generally structured like this:
with cte as (
priming select
union all
secondary select)
select * from cte
So to get a BOM from an Item Master that looks something like:
CREATE TABLE item (
ItemNo Char(10) PRIMARY KEY,
Description Char(50));
INSERT INTO item
VALUES ('Item0', 'Root Item'),
('Item1a', 'Second Level Item'),
('Item1b', 'Another Second Level Item'),
('Item2a', 'Third Level Item');
and a linkage table like this:
CREATE TABLE linkage (
PItem Char(10),
CItem Char(10),
Quantity Dec(5,0),
PRIMARY KEY (PItem, CItem));
INSERT INTO linkage
VALUES ('Item0', 'Item1a', 2),
('Item0', 'Item1b', 3),
('Item1b', 'Item2a', 5)
The recursive CTE to list a BOM for 'Item0' looks like this:
WITH bom (Level, ItemNo, Description, Quantity)
AS (
-- Load BOM with root item
SELECT 0,
ItemNo,
Description,
1
FROM Item
WHERE ItemNo = 'Item0'
UNION ALL
-- Retrieve all child items
SELECT a.Level + 1,
b.CItem,
c.Description,
a.Quantity * b.Quantity
FROM bom a
join linkage b ON b.pitem = a.itemno
join item c ON c.itemno = b.citem)
-- Set the list order
SEARCH DEPTH FIRST BY itemno SET seq
-- List BOM
SELECT * FROM bom
ORDER BY seq
Here are my results:
LEVEL
ITEMNO
DESCRIPTION
QUANTITY
0
Item0
Root Item
1
1
Item1a
Second Level Item
2
1
Item1b
Another Second Level Item
3
2
Item2a
Third Level Item
15
Notice the search clause, that generates a column named seq which you can use to sort the output either depth first or breadth first. Depth first is what you want here.
NOTE: This isn't necessarily an optimum query since the description is in the CTE, and that increases the size of the CTE result set without really adding anything to it that couldn't be added in the final select. But it does make things a bit simpler since the 'priming query' retrieves the description.
Note also: the column list on the with clause following BOM. This is there to remove the confusion that DB2 had with the expected column list when the explicit column list was omitted. It is not always necessary, but if DB2 complains about an invalid column list, this will fix it.

SQL Partition By Query

Looking for help with a TSQL query.
Lets say you are building Kitchen Cabinets. There are numerous parts to build 1 cabinet and a kitchen in turn will have numerous cabinets. Lets also say that we have 3 different types of cabinets (floor, upper and tall).
We have rack(s) where we want to put all the parts required to build the kitchen. However, we want to keep everything organized. So, Cabinet 1, we want to keep all its parts together.
Our Rack has numerous slots (so many per type of cabinet..upper, floor, tall). The slots can only hold so many pieces before it needs to spill over to the next slot.
So our database has a list of all the parts required per cabinet that is needed. The goal is to associate each part with a rack and slot #. We also know that there are cases where 1 cabinet will need numerous slots and a kitchen may require numerous racks.
How can you write a query to do this. Sample data below:
So The goal (referencing the row number - slot number).
1-1, 2-2, 3-3, 4-3, 5-3, 6-3, 7-3, 8-3, 9-4, 10-4......
Cabinet ID 32 needs 3 slots. So we want to show that you are going to start in slot 3, fill it, move to slot 4, fill it and then move on to slot 5.
I have separate tables that tell me how many slots per slot type are in a rack.
I am using the following so far:
SELECT TOP (100) PERCENT dbo.[CAD-CV-Cabinets].[Cabinet ID] ,
dbo.[CAD-CV-Parts].Width ,
dbo.[CAD-CV-Parts].Length ,
dbo.[CAD-CV-MateriaIs].Name ,
dbo.[CAD-CV-Parts].AutoID ,
dbo.[CAD-CV-Cabinets].[Cabinet Type] ,
CASE WHEN [CAD-CV-Parts].Width > 40
OR [CAD-CV-Parts].Width > 40
OR [CAD-CV-Cabinets].[Cabinet Type] IN(3) THEN 'Tall'
WHEN [CAD-CV-Cabinets].[Cabinet Type] IN(1 , 12 , 4 , 5 , 6 , 7) THEN 'Lower'
WHEN [CAD-CV-Cabinets].[Cabinet Type] IN(2 , 9 , 8 , 10 , 11 , 13 , 14) THEN 'Upper'
ELSE 'No Cart' END AS SlotType,
CASE WHEN [CAD-CV-Parts].Width > 40
OR [CAD-CV-Parts].Width > 40
OR [CAD-CV-Cabinets].[Cabinet Type] IN(3)
THEN ceiling(SUM(2) OVER (PARTITION BY [CAD-CV-Cabinets].[Cabinet ID] ORDER BY dbo.[CAD-CV-Parts].AutoID) / (select min(SlotWidth) from CartDetails where SlotType = 'Tall'))
end TallSlotCount FROM dbo.[CAD-CV-Cabinets]
INNER JOIN
dbo.[CAD-CV-Parts]
ON dbo.[CAD-CV-Cabinets].[Cabinet ID] = dbo.[CAD-CV-Parts].CabinetID
INNER JOIN
dbo.[CAD-CV-MateriaIs]
ON dbo.[CAD-CV-Parts].MaterialID = dbo.[CAD-CV-MateriaIs].ID WHERE ([CAD-CV-Parts].Width > 40
OR [CAD-CV-Parts].Width > 40
OR [CAD-CV-Cabinets].[Cabinet Type] IN(3))
and dbo.[CAD-CV-Parts].DoorID = 0
AND dbo.[CAD-CV-Parts].DrawerID = 0
AND dbo.[CAD-CV-Parts].RollOutID = 0
AND dbo.[CAD-CV-Parts].TopID = 0 ORDER BY dbo.[CAD-CV-Cabinets].[Cabinet ID] , dbo.[CAD-CV-Parts].AutoID;

how to create multiple Concatenation on multiple left joins in a query

I am trying to left join table HE to table S as shown in the code below.
In order for me to get a match and join in the data, i have to concat client id and market as shown below in [Headendmarket&tbcode] and [SETClientID+Market] from both S and HE tables.
but because some of the data in the columns are not clean. the concat above is not enough and there are some missing things coming in S in the join.
the solution for me would be to further vlook up the data for the data that is left null after running the query below and do another concat on S.[Market]&s.[client] and leftjoin it further with HE.[HeadendMarket] & [HE.Advertisername]. this is going to match the remaining data thats null to see if there is a possible match and bring back the data it can for the S table.
not sure what is the best way for me to do this in this query?
SELECT DISTINCT CONCAT(HE.[AdvertiserTBCode],HE.[HeadendMarket]) AS [Headendmarket&tbcode]
,CONCAT(S.[CLIENTID],S.[Market]) AS [SETClientID+Market]
,HE.[CurrentRegionName]
,HE.[CurrentMarketName]
,HE.[CurrentSalesTeamName]
,HE.[CurrentSalesOfficeName]
,HE.[CurrentCorpAEName]
,HE.[CurrentAEType]
,HE.[AdvertiserTBCode] --Has all the correct data--
,HE.[AdvertiserName]
,HE.[ParentAdvertiserName]
,HE.[HeadendRegion]
,HE.[HeadendMarket] --has all correct data--
,HE.[CorpCategoryGroup]
,S.[Actuals vs projections]
,S.[Year]
,S.[Month]
,S.[Area]
,CASE S.[Market] --some markets not the same name which is why we are doing this to have the same markets as HE Table--
WHEN 'Twin Cities' THEN 'Minneapolis - St. Paul'
WHEN 'Fort Myers' THEN 'Ft. Myers - Naples'
WHEN 'Bowling Green' THEN 'Nashville'
WHEN 'North Miss' THEN 'TUPELO'
WHEN 'Monroe, LA' THEN 'Monroe'
WHEN 'Southern Miss-Hattiesburg/Laurel/Meridian' THEN 'SOUTHERN MISS'
WHEN 'Northern Miss-Columbus/Tupelo' THEN 'Tulepo'
WHEN 'Little Rock, AR' THEN 'Little Rock'
WHEN 'Fort Wayne' THEN 'Ft. Wayne'
WHEN 'Wheeling/Youngstown/Canfield' THEN 'WYC'
WHEN 'Johnstown/Altoona/State College' THEN 'Johnstown-Altoona'
WHEN 'Washington, D.C.' THEN 'Washington'
ELSE S.[Market] END AS [SET Market]
,S.[Zone Type]
,S.[Category]
,S.[Subcategory]
,S.[Event]
,S.[Network]
,S.[AE]
,S.[Client] -- some wrong fields--
,S.[ClientID] --some incorrect data entered. this with the market concatenation should match the data in the HE table in a perfect world.--
,S.[# Spots]
,S.[Gross ($)]
FROM [REO].[dbo].[Sports] S
LEFT JOIN [REO].[dbo].[HF_Final] HE
ON CONCAT(S.[CLIENTID],CASE S.[Market]
WHEN 'Twin Cities' THEN 'Minneapolis - St. Paul'
WHEN 'Fort Myers' THEN 'Ft. Myers - Naples'
WHEN 'Bowling Green' THEN 'Nashville'
WHEN 'North Miss' THEN 'TUPELO'
WHEN 'Monroe, LA' THEN 'Monroe'
WHEN 'Southern Miss-Hattiesburg/Laurel/Meridian' THEN 'SOUTHERN MISS'
WHEN 'Northern Miss-Columbus/Tupelo' THEN 'Tulepo'
WHEN 'Little Rock, AR' THEN 'Little Rock'
WHEN 'Fort Wayne' THEN 'Ft. Wayne'
WHEN 'Wheeling/Youngstown/Canfield' THEN 'WYC'
WHEN 'Johnstown/Altoona/State College' THEN 'Johnstown-Altoona'
WHEN 'Washington, D.C.' THEN 'Washington'
ELSE S.[Market] END)
= CONCAT(HE.[AdvertiserTBCode],HE.[HeadendMarket])
Please identify ALL Table&Column(s) that do not have "Clean Data".
And give examples--eg Bad=;ShouldBe=. These columns may affect how we proceed. And, get rid of the extraneous columns that are not part of the problem.
In the meantime, you can identify the exact dirty data and add some more conditioning... Note: this sql will allow you to see nulls on either side of the ON condition. That is, some HE data may not match because the S data is incorrect.
SELECT
AllKeys.AdvertiserTBCode
,AllKeys.Market
,S.AdvertiserTBCode
.S.Market
,HE.CLIENTID
.HE.Market
From
-- get all of the possible keys from each side
(SELECT DISTINCT
,AdvertiserTBCode
,Market -- or CASE Market...
FROM [REO].[dbo].[Sports]
UNION
SELECT
CLIENTID
,Market -- or CASE Market...
FROM [REO].[dbo].[HF_Final]
) as AllKeys
-- join each side to its keys
LEFT JOIN [REO].[dbo].[Sports] S
ON AllKeys.AdvertiserTBCode = S.AdvertiserTBCode
And AllKeys.Market = S.Market
LEFT JOIN [REO].[dbo].[HF_Final] HE
ON AllKeys.AdvertiserTBCode = HE.CLIENTID
And AllKeys.Market = HE.Market
Order By AllKeys.AdvertiserTBCode, AllKeys.Market
-- now filter those that you want to pay attention to
--Having maybe some nulls in certain fields