Check 1:N hierarchy is maintained at 4 levels - sql

I have hierarchy level data of 5 levels in a table
1.BRANCH
2.LEGAL-ENTITY
3.REPORT-UNIT
4.REGIONAL
5.COUNTRY
I have a table which contain records containing all the hierarchy with Branch as the Primary Key.
I need to check if the relationship of 1:n is maintained between higher level hierarchy to low level hierarchy.
WHITEFIELD|BANGALORE|KARNATAKA|INDIA|APAC
WHITEFIELD|BANGALORE|MYSORE|INDIA|APAC - WRONG RECORD
MG ROAD|BANGALORE|KARNATAKA|INDIA|APAC
MG ROAD|NEW DELHI|DELHI|INDIA|APAC - WRONG RECORD
SILK BOARD|BANGALORE|KARNATAKA|INDIA|APAC
SILK BOARD|PUNE|MAHARASTRA|INDIA|APAC - WRONG RECORD
To achieve this, I want to write one query which can give me records which are not satisfying the above rule.
I have already written a query which can give the result for 4th and 5th level.
SELECT COD_BRNC,COUNT(DISTINCT COD_LEGL_ENTT) FROM TDI_GEO_BY_ORIGIN
WHERE COD_BRNC NOT LIKE '%_D' AND YEAR(DAT_END_GEO_ORGN ) = 9999
GROUP BY COD_BRNC HAVING COUNT(DISTINCT COD_LEGL_ENTT) > 1
It gives me
SILK BOARD|2
MG ROAD|2

Your first query is a good start.
As you did not give a complete Table description I created my own testcase:
DROP TABLE level_5_test;
CREATE TABLE level_5_test( branch_id VARCHAR2( 3 )
, level_1 VARCHAR2( 3 )
, level_2 VARCHAR2( 3 )
, level_3 VARCHAR2( 3 )
, level_4 VARCHAR2( 3 )
, description VARCHAR2( 30 )
);
INSERT INTO level_5_test
VALUES ( 'B0', 'L1', 'L2', 'L3', 'L4', 'No Problems' );
INSERT INTO level_5_test
VALUES ( 'B1', 'L1', 'L2', 'L3', 'L4', 'Level 1 Problems' );
INSERT INTO level_5_test
VALUES ( 'B1', 'E1', 'L2', 'L3', 'L4', 'Level 1 Problems' );
INSERT INTO level_5_test
VALUES ( 'B2', 'L1', 'L2', 'L3', 'L4', 'Level 2 Problems' );
INSERT INTO level_5_test
VALUES ( 'B2', 'L1', 'E2', 'L3', 'L4', 'Level 2 Problems' );
INSERT INTO level_5_test
VALUES ( 'B3', 'L1', 'L2', 'L3', 'L4', 'Level 3 Problems' );
INSERT INTO level_5_test
VALUES ( 'B3', 'L1', 'L2', 'E3', 'L4', 'Level 3 Problems' );
INSERT INTO level_5_test
VALUES ( 'B4', 'L1', 'L2', 'L3', 'L4', 'Level 4 Problems' );
INSERT INTO level_5_test
VALUES ( 'B4', 'L1', 'L2', 'L3', 'E4', 'Level 4 Problems' );
INSERT INTO level_5_test
VALUES ( 'B5', 'L1', 'L2', 'L3', 'E4', 'Double Problems' );
INSERT INTO level_5_test
VALUES ( 'B5', 'L1', 'E2', 'L3', 'L4', 'Double Problems' );
COMMIT;
--
The best way to get all the answers is by querying all fields and adding the analytic variations of count(distinct...) where you partition by the branch_id:
WITH level_query
AS (SELECT branch_id
, level_1
, level_2
, level_3
, level_4
, description
, COUNT( DISTINCT level_1 ) OVER (PARTITION BY branch_id)
AS level_1_count
, COUNT( DISTINCT level_2 ) OVER (PARTITION BY branch_id)
AS level_2_count
, COUNT( DISTINCT level_3 ) OVER (PARTITION BY branch_id)
AS level_3_count
, COUNT( DISTINCT level_4 ) OVER (PARTITION BY branch_id)
AS level_4_count
FROM level_5_test)
SELECT *
FROM level_query
WHERE level_1_count > 1
OR level_2_count > 1
OR level_3_count > 1
OR level_4_count > 1;
This works for Oracle, but might not work (yet) for Hana. Then you have to combine four queries with UNION ALL
SELECT 'L1' AS level_name
, branch_id
FROM level_5_test
GROUP BY branch_id
HAVING COUNT( DISTINCT level_1 ) > 1
UNION ALL
SELECT 'L2' AS level_name
, branch_id
FROM level_5_test
GROUP BY branch_id
HAVING COUNT( DISTINCT level_2 ) > 1
UNION ALL
SELECT 'L3' AS level_name
, branch_id
FROM level_5_test
GROUP BY branch_id
HAVING COUNT( DISTINCT level_3 ) > 1
UNION ALL
SELECT 'L4' AS level_name
, branch_id
FROM level_5_test
GROUP BY branch_id
HAVING COUNT( DISTINCT level_4 ) > 1
ORDER BY 2, 1;
The first solution needs only one full table scan, the second query needs four and would need a fifth FTS to give as much information as the first!

Related

Delete query is not working with order by for multiple columns

CREATE TABLE SAMPLE1
(
CN VARCHAR(MAX),
CR VARCHAR(MAX),
DN VARCHAR(MAX),
DR VARCHAR(MAX),
DMR VARCHAR(MAX)
)
INSERT INTO SAMPLE1 VALUES ('C1', 'A', 'D1', '--', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'B', 'D1', '-A', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'E', 'D2', '--', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'C', 'D1', '-A', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'D', 'D3', '--', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'F', 'D2', '--', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'F', 'D2', '-A', '--')
Expected result:
('C1', 'F', 'D2', '-A', '--')
i.e last record from SAMPLE1.
I tried with the following query, but it doesn't work:
DELETE t
FROM
(SELECT
ROW_NUMBER() OVER(PARTITION BY CN
ORDER BY CR, DR, DMR DESC) AS r
FROM SAMPLE1) t
WHERE r > 1
Given that you are using SQL Server, you could place the logic from your current query into a deletable CTE:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CN
ORDER BY CR DESC, DR DESC, DMR DESC) rn
FROM SAMPLE1
)
DELETE
FROM cte
WHERE rn > 1;

T-SQL how to filter by multiple criteria but prioritize what is returned?

I have the data below.
create table #results (
id int
, result_attr char(1)
, result varchar(100)
)
insert into
#results (id, result_attr, result)
values
(1, 'E', '***ERROR')
, (2, 'E', '***CORRECTED')
, (3, 'E', '***RESULTED')
, (4, 'E', '***AMENDED')
, (4, 'E', 'FOO')
, (5, 'E', 'ERROR***')
, (5, 'E', 'CORPOREAL')
, (6, 'E', '***CORRECTED')
, (7, 'E', '***RESULTED')
, (7, 'E', 'ABUNDANT')
, (7, 'E', 'PLENTITUDE')
, (8, 'E', 'INCORRECT')
, (9, 'A', 'HIGH')
, (10, 'A', 'LOW')
select *
from #results
drop table #results
The complete result set is:
My desired result set is:
This doesn't quite work:
select
res.id
, res.result_attr
, res.result
from #results as res
where
(charindex('***', res.result) > 0 or res.result_attr = 'E')
Tricky part being that I would want to exclude ID #4 with result "FOO" as well as ID #5 with result "CORPOREAL" and ID #7 with results "ABUNDANT" and "PLENTITUDE", but I want to keep ID #8 with result "INCORRECT". All in all, I want to exclude the following:
I've tried some windowing functions and other things, but am a bit stuck on this one. I would appreciate any assistance!
You can use row_number analytical function with conditional ordering as follows:
Select * from
(select
res.id
, res.result_attr
, res.result
, row_number() over (partition by res.id
order by case when charindex('***', res.result) > 0
then 1 else 2 end) as rn
from #results as res
where
(charindex('***', res.result) > 0 or res.result_attr = 'E') t
Where rn = 1
Order by id
Using:
select
res.id
, res.result_attr
, res.result
from results as res
where res.result LIKE '%***%' OR res.result = 'INCORRECT';
db<>fiddle demo

SQL Server: how to form a crosstab

I am using SQL Server 2014.
How can I create a cross-table out of the straight table like the below?
Code here where I try to do the multiple-pivoting.
Sample input
Code here only about the sample data.
GOAL after pivoting and reformatting of the columns by other columns
This logic would work for you:
Create an additional temp table where you store all the possible combinations of Store and City, then left join with your existing table.
From this left join you get the "calculated" string that you will use as a column name (C1_L_xCount, C1_L2_xCount, etc).
Then apply a pivot as below:
select ylabel.colx, ylabel.coly, y.myid, y.week, isnull(y.xCount, 0) xCount, isnull(y.yCount, 0) yCount
into #table
from (
select distinct y3.week, y1.city, y2.store, y1.city + '_' + y2.store + '_xCount' as colx, y1.city + '_' + y2.store + '_yCount' as coly
from #yt y1 cross join #yt y2 cross join #yt y3
) ylabel left join #yt y on y.week = ylabel.week and y.store = ylabel.store and y.city = ylabel.city
select * from #table --this is the additional table, the one used for pivoting
--this is your solution:
select myid
, week
, isnull(C1_L_xCount, 9) C1_L_xCount
, isnull(C1_L2_xCount, 9 ) C1_L2_xCount
, isnull(C2_L_xCount, 0) C2_L_xCount
, isnull(C2_L2_xCount, 0) C2_L2_xCount
, isnull(C1_L_yCount, 0) C1_L_yCount
, isnull(C1_L2_yCount, 0) C1_L2_yCount
, isnull(C2_L_yCount, 0) C2_L_yCount
, isnull(C2_L2_yCount, 0) C2_L2_yCount
from
(
select *
from #table
pivot ( max(xCount) for colx in ( [C1_L_xCount], [C1_L2_xCount],[C2_L_xCount], [C2_L2_xCount])) p
pivot ( max(yCount) for coly in ( [C1_L_yCount], [C1_L2_yCount],[C2_L_yCount], [C2_L2_yCount])) q
where myid is not null
) t
Please check a working demo here.
But, if you need to dynamically add Stores and Cities, you will need to convert this into a dynamic pivot.
We repeat the case such that
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[xCount]
END
AS 'LC1_xCount',
I changed the valuations a little bit to get more combinations, code here.
or code here:
CREATE TABLE #yt
([MyID] int, [Store] nvarchar(300), [City] nvarchar(300), [Week] int, [xCount] int, [yCount] int)
;
INSERT INTO #yt
([MyID], [Store], [City], [Week], [xCount], [yCount])
VALUES
(1, 'L', 'C1', 1, 96, 7),
(2, 'L', 'C1', 1, 138, 77),
(3, 'L2', 'C1', 1, 37, 744),
(4, 'L', 'C1', 1, 59, 74),
(5, 'L', 'C1', 2, 282,73333),
(6, 'L2', 'C2', 2, 212,7333),
(7, 'L2', 'C2', 2, 78,733),
(8, 'L', 'C2', 2, 97,73),
(9, 'L', 'C2', 3, 60,72222),
(10, 'L2', 'C2', 3, 123,7222),
(11, 'L2', 'C1', 3, 220,722),
(12, 'L2', 'C1', 3, 87,72)
;
select [MyId], [Week], [LC1_xCount], [LC2_xCount], [L2C1_xCount], [L2C2_xCount]
, [LC1_yCount], [LC2_yCount], [L2C1_yCount], [L2C2_yCount]
from
(
select myid, week, store, city, xcount, ycount,
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[xCount]
END
AS 'LC1_xCount',
CASE WHEN
[City]='C2' AND [Store]='L'
THEN
[xCount]
END
AS 'LC2_xCount',
CASE WHEN
[City]='C1' AND [Store]='L2'
THEN
[xCount]
END
AS 'L2C1_xCount',
CASE WHEN
[City]='C2' AND [Store]='L2'
THEN
[xCount]
END
AS 'L2C2_xCount',
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[yCount]
END
AS 'LC1_yCount',
CASE WHEN
[City]='C2' AND [Store]='L'
THEN
[yCount]
END
AS 'LC2_yCount',
CASE WHEN
[City]='C1' AND [Store]='L2'
THEN
[yCount]
END
AS 'L2C1_yCount',
CASE WHEN
[City]='C2' AND [Store]='L2'
THEN
[yCount]
END
AS 'L2C2_yCount'
from #yt
GROUP BY myid, week, store,city, xcount, ycount
) src;

LEAD/LAG SQL Server 2012/Gaps and Islands

I'm having a few issues with LEAD/LAG. For each row within a set of IDs I'm wanting to get the previous/next source where isAQI = 1. Desired output is as follows in prevAQI and nextAQI columns.
I've tried the same approach as Lag() with conditon in sql server, but with no luck. Any help would be much appreciated!
Sample data as follows:
DECLARE #a TABLE ( id int, timest datetime, source char(2),
isAQI int, prevAQI char(2), nextAQI char(2))
INSERT #a VALUES
(6694 ,'2015-06-11 08:55:06.000' ,'I' ,1, NULL, 'A'),
(6694 ,'2015-06-11 09:00:00.000' ,'A' ,1, 'I', 'I'),
(6694 ,'2015-06-11 09:11:49.000' ,'C' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 09:29:06.000' ,'O' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 09:29:06.000' ,'DT' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 09:34:11.000' ,'DT' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 09:34:11.000' ,'O' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 10:06:27.000' ,'I' ,1, 'A', 'I'),
(6694 ,'2015-06-11 11:25:09.000' ,'DT' ,NULL, 'I', 'I'),
(6694 ,'2015-06-11 18:25:24.000' ,'C' ,NULL, 'I', 'I'),
(6694 ,'2015-06-12 17:57:16.000' ,'I' ,1, 'I', NULL);
SELECT *
FROM #a
See if the following query works for you:
WITH C AS
(
SELECT *,
MAX(goodval) OVER(PARTITION BY id
ORDER BY timest
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS prv,
MIN(goodval) OVER(PARTITION BY id
ORDER BY timest
ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) AS nxt
FROM #a
CROSS APPLY ( VALUES( CONVERT(VARCHAR(23), timest, 121)
+ CASE WHEN isAQI = 1 THEN source END ) ) AS A(goodval)
)
SELECT id, timest, source,
CASE WHEN prv IS NOT NULL THEN SUBSTRING(prv, 24, 2) END AS prevAQI,
CASE WHEN nxt IS NOT NULL THEN SUBSTRING(nxt, 24, 2) END AS nextAQI
FROM C
ORDER BY id, timest;

how to select the min value using having key word

I have created the table stu_dep_det
CREATE TABLE `stu_dept_cs` (
`s_d_id` int(10) unsigned NOT NULL auto_increment,
`stu_name` varchar(15) , `gender` varchar(15) , `address` varchar(15),`reg_no` int(10) ,
`ex_no` varchar(10) ,
`mark1` varchar(10) ,
`mark2` varchar(15) ,
`mark3` varchar(15) ,
`total` varchar(15) ,
`avg` double(2,0),
PRIMARY KEY (`s_d_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC AUTO_INCREMENT=8 ;
then Inserted the values
INSERT INTO `stu_dept_cs` (`s_d_id`, `stu_name`, `gender`, `address`, `reg_no`, `ex_no`, `mark1`, `mark2`, `mark3`, `total`, `avg`) VALUES
(1, 'alex', 'm', 'chennai', 5001, 's1', '70', '90', '95', '255', 85),
(2, 'peter', 'm', 'chennai', 5002, 's1', '80', '70', '90', '240', 80),
(6, 'parv', 'f', 'mumbai', 5003, 's1', '88', '60', '80', '228', 76),
(7, 'basu', 'm', 'kolkatta', 5004, 's1', '85', '95', '56', '236', 79);
I want to select the min(avg) using having keyword and I have used the following sql statement
SELECT * FROM stu_dept_cs s having min(avg)
Is it correct or not plz write the correct ans....
select somecolumn1,somecolumn2
from stu_dept_cs
group by somecolumn1,somecolumn2,avg
having avg = min(avg)
or
with t1
(select rownumber() over (partition by somecolumn1,somecolumn2
order by somecolumn1,somecolumn2,avg asc) as rownum
from stu_dept_cs )
select * from t1 where rownum=1
SELECT t1.* FROM stu_dept_cs t1
LEFT JOIN stu_dept_cs t2
ON t1.avg > t2.avg
WHERE t2.stu_name IS NULL;