SQL Group By specific column with nullable - sql

Let's say I have this data in my table A:
group_id
type
active
1
A
true
1
B
false
1
C
true
2
null
false
3
B
true
3
C
false
I want to create a query which return the A row if exists (without the type column), else return a row with active false.
For this specific table the result will be:
group_id
active
1
true
2
false
3
false
How can I do this ?
I'm assuming I have to use a GROUP BY but I can't find a way to do it.
Thank you

This is a classic row_number problem, generate a row number based on your ordering criteria, then select just the first row in each grouping.
declare #MyTable table (group_id int, [type] char(1), active bit);
insert into #MyTable (group_id, [type], active)
values
(1, 'A', 1),
(1, 'B', 0),
(1, 'C', 1),
(2, null, 0),
(3, 'B', 1),
(3, 'C', 0);
with cte as (
select *
, row_number() over (
partition by group_id
order by case when [type] = 'A' then 1 else 0 end desc, active asc
) rn
from #MyTable
)
select group_id, active
from cte
where rn = 1
order by group_id;
Returns:
group_id
active
1
1
2
0
3
0
Note: Providing the DDL+DML as I have shown makes it much easier for people to assist.

This should do it. We select all the distinct group_ids and then join our table back to that. There is an ISNULL function that will insert the 'false' when 'A' type records are not found.
DECLARE #tableA TABLE (
group_id int
, [type] nchar(1)
, active nvarchar(10)
);
INSERT INTO #tableA (group_id, [type], active)
VALUES
(1, 'A', 'true')
, (1,'B','false')
, (1,'C', 'false')
, (2, null, 'false')
, (3, 'B', 'true')
, (3, 'C', 'false')
;
SELECT
gid.group_id
, ISNULL(a.active,'false') as active
FROM (SELECT DISTINCT group_id FROM #tableA) as gid
LEFT OUTER JOIN #tableA as a
ON a.group_id = gid.group_id
AND a.type = 'A'

Related

SQL Server query to extract all rows

I've two database tables, one called "Headers" and one called "Rows".
The structure is:
Header: IDPK | Description
Row: IDPK | IDPK_Header | Item_ID | Qty
I need to do a query that says: "From a Header, IDPK find another header that have the same number of rows and the same item ID and quantity".
For example:
Header Rows
IDPK Description IDPK Item_ID Qty
1 'Test1' 1 'A' 10
1 'Test1' 2 'B' 20
2 'Test2' 3 'A' 10
2 'Test2' 4 'B' 20
3 'Test3' 5 'A' 5
3 'Test3' 6 'B' 20
4 'Test4' 7 'A' 10
Header Test1 match Test2 but not Test3 and Test4
The problem is that the number of rows must be exactly the same. I try with ALL operator but without luck.
How I can do the query with an eye for the performance? The two tables can be very huge (~500.000 records).
Assuming there are no duplicates:
with r as (
select r.*, count(*) over (partition by idpk_header) as num_items
from rows r
)
select r1.idpk_header, r2.idpk_header
from r r1 join
r r2
on r1.item_id = r1.item_id and r2.qty = r1.qty and r2.num_items = r1.num_items
group by r1.idpk_header, r2.idpk_header, r1.num_items
having count(*) = r1.num_items;
Basically, this does a self-join on the items, so you only get matches. The on validates that the two have the same number of items. And the having guarantees that all match.
Note: This version returns each match of the header to itself. That is a nice check. You can of course filter this out in the on or a where clause.
If you do have duplicate items, you can simply replace r with:
select idpk_header, item_id, sum(qty) as qty,
count(*) over (partition by idpk_header) as num_items
from rows r
group by idpk_header, item_id;
I woul suggest using a forxml query in order to create the list of items per IDPK. Next I would search for matching item lists and quantities. See following example:
DECLARE #Headers TABLE(
IDPK INT,
Description NVARCHAR(100)
)
DECLARE #Rows TABLE(
IDPK INT,
ITEMID NVARCHAR(1),
Qty INT
)
INSERT INTO #Headers VALUES
(1, 'Test1'),
(2, 'Test2'),
(3, 'Test3'),
(4, 'Test4'),
(5, 'Test5')
INSERT INTO #Rows VALUES
(1, 'A', 10),
(1, 'B', 20),
(2, 'A', 10),
(2, 'B', 20),
(3, 'A', 5 ),
(3, 'B', 20),
(4, 'C', 10),
(5, 'A', 10),
(5, 'C', 20)
;
WITH cteHeaderRows AS(
SELECT IDPK
,ItemIDs=STUFF(
(
SELECT ',' + CAST(ITEMID AS VARCHAR(MAX))
FROM #Rows t2
WHERE t2.IDPK = t1.IDPK
ORDER BY ITEMID, QTY
FOR XML PATH('')
),1,1,''
)
,Qtys=STUFF(
(
SELECT ',' + CAST(Qty AS VARCHAR(MAX))
FROM #Rows t2
WHERE t2.IDPK = t1.IDPK
ORDER BY ITEMID, QTY
FOR XML PATH('')
),1,1,''
)
FROM #Rows t1
GROUP BY IDPK
),
cteFilter AS(
SELECT h1.IDPK AS IDPK1, h2.IDPK AS IDPK2
FROM cteHeaderRows h1
JOIN cteHeaderRows h2 ON h1.IDPK != h2.IDPK AND h1.ItemIDs = h2.ItemIDs AND h2.Qtys = h1.Qtys
)
SELECT DISTINCT h.IDPK, h.Description, r.ItemID, r.Qty
FROM #Headers h
JOIN cteFilter f ON f.IDPK1 = h.IDPK
JOIN #Rows r ON r.IDPK = f.IDPK1
ORDER BY 1,3,4

How to get the ID from the table which is not mention EndDate

How to get the name from the table which is not having EndDate
in the above pic i need to get D and G details from the table ,
( To understand mOre:
A, C,D,G are having end date, and A, C are again started, but D and G is not started, so from the query i need to get the name D and G
the code i used is not works for it
DECLARE #T AS TABLE
(
SubInventoryID int ,
SubInventoryName varchar(20),
RolesName varchar(20),
StartDate date,
EndDate date
)
INSERT INTO #T VALUES
(30,'RIF-Teller','Teller', '2016-12-27', '2017-01-23'),
(30,'RIF-Teller','Teller', '2016-12-08', NULL),
(30,'RIF-Teller','Teller', '2017-01-02', '2017-01-05'),
(31,'RIF-Teller','Teller', '2017-01-05', NULL),
(24,'MHQ-Teller','Teller', '2016-09-20', '2017-01-23'),
(24,'MHQ-Teller','Teller', '2016-08-01', '2017-01-05'),
(24,'MHQ-Teller','Teller', '2017-01-05', NULL)
Query
SELECT UP.SubInventoryID,S.SubInventoryName SubInventoryName,RolesName,UP.StartDate StartDate,
UP.EndDate EndDate , case when UP.EndDate IS null then 'Occupied' else 'Closed' End As Vacancy
FROM [View_Alx_UserPosition] UP
Inner join ALX_Branches B ON B.BranchID= UP.BranchID
Inner join ALX_SubInventories S ON S.SubInventoryID=UP.SubInventoryID WHERE UP.RolesName Like '%Teller%'
union
SELECT distinct(UP.SubInventoryID),S.SubInventoryName SubInventoryName, '' FullName, '' RolesName,NUll StartDate,
NUll EndDate,'Free' as vacancy
FROM [View_Alx_UserPosition] UP
Inner join ALX_Branches B ON B.BranchID= UP.BranchID
Inner join ALX_SubInventories S ON S.SubInventoryID=UP.SubInventoryID
WHERE UP.EndDate IS NOT NULL ANd UP.RolesName Like '%Teller%'
AND NOT EXISTS
(
SELECT 1
FROM [View_Alx_UserPosition] UP1
WHERE UP1.SubInventoryID = UP.SubInventoryID
AND UP1.StartDate >= UP.EndDate
-- AND UP1.EndDate IS NOT NULL
)
Update
Create and populate sample table (Please save us this step in your future questions)
DECLARE #T AS TABLE
(
ID int identity(1,1),
Name char(1),
StartDate date,
EndDate date
)
INSERT INTO #T VALUES
('A', '2016-04-04', '2017-04-03'),
('B', '2016-04-04', NULL),
('C', '2016-04-04', '2017-04-03'),
('D', '2016-04-04', '2017-04-03'),
('E', '2016-04-04', NULL),
('F', '2016-04-04', NULL),
('G', '2016-04-04', '2017-04-03'),
('C', '2017-04-03', NULL),
('A', '2017-04-03', NULL)
The query:
SELECT Name
FROM #T vu1
WHERE EndDate IS NOT NULL
AND NOT EXISTS
(
SELECT 1
FROM #T vu2
WHERE vu2.Name = vu1.Name
AND vu2.StartDate >= vu1.EndDate
)
Results:
Name
D
G
First version
Assuming I understand the question, this should do the trick:
SELECT Name
FROM View_User vu1
WHERE EndDate IS NOT NULL
AND NOT EXISTS
(
SELECT 1
FROM View_User vu2
WHERE vu2.Name = vu1.Name
AND vu2.StartDate >= vu1.EndDate
AND vu2.EndDate IS NOT NULL
)

Counting and grouping challenge in a pivot table with T-SQL

I have a pivot table that converts a vertical database design to a horizontal one:
The source table:
Id ParentId Property Value
---------------------------------
1 1 Date 01-09-2015
2 1 CountValue 2
3 1 TypeA Value1
4 1 TypeB Value2
5 1 TypeC Value2
6 2 Date 15-10-2015
7 2 CountValue 3
8 2 TypeA Value3
9 2 TypeB Value22
10 2 TypeC Value99
After pivoting this looks like:
ParentId Date CountValue TypeA TypeB TypeC
----------------------------------------------------------
1 01-09-2015 2 Value1 Value2 Value2
2 15-10-2015 3 Value3 Value22 Value99
Then, there's a look-up table for valid values in columns TypeA, TypeB and TypeC:
Id Name Value
-----------------
1 TypeA Value1
2 TypeA Value2
3 TypeA Value3
4 TypeB Value20
5 TypeB Value21
6 TypeB Value22
7 TypeC Value1
8 TypeC Value2
So, given the above structure I'm looking for a way to query the pivot table in a way that I'll get a count of all invalid values in TypeA, TypeB and TypeC where Date is a valid date and CountValue is not empty and greater than 0.
How can I achieve a result that is expected and outputted like below:
Count Column
--------------
0 TypeA
1 TypeB
1 TypeC
I've accomplished the result by creating three several queries and glue the results using UNION, but I think it should also be possible using the pivot table, but I'm unsure how. Can the desired result be realized using the pivot table?
Note: the database used is a SQL Server 2005 database.
I would not approach this a PIVOT, otherwise you have to pivot your data, then unpivot it to get the output required. Breaking it down step by step you can get your valid parent IDs using this:
SELECT t.ParentID
FROM #T AS t
GROUP BY t.ParentID
HAVING ISDATE(MAX(CASE WHEN t.Property = 'Date' THEN t.Value END)) = 1
AND MAX(CASE WHEN t.Property = 'CountValue' THEN CONVERT(INT, t.Value) END) > 0;
The two having clauses limit this to your criteria of having a valid date, and a CountValue that is greater than 0
The next step would be to find your invalid properties:
SELECT t.*
FROM #T AS t
WHERE NOT EXISTS
( SELECT 1
FROM #V AS v
WHERE v.Name = t.Property
AND v.Value = t.Value
);
This will include Date, and CountValue, and also won't include TypeA because all the properties are valid, so a bit more work is required, we must find the distinct properties we are interested in:
SELECT DISTINCT Name
FROM #V
Now we can combine this with the invalid properties to get the count, and with the valid parent IDs to get the desired result:
WITH ValidParents AS
( SELECT t.ParentID
FROM #T AS t
GROUP BY t.ParentID
HAVING ISDATE(MAX(CASE WHEN t.Property = 'Date' THEN t.Value END)) = 1
AND MAX(CASE WHEN t.Property = 'CountValue' THEN CONVERT(INT, t.Value) END) > 0
), InvalidProperties AS
( SELECT t.Property
FROM #T AS t
WHERE t.ParentID IN (SELECT vp.ParentID FROM ValidParents AS vp)
AND NOT EXISTS
( SELECT 1
FROM #V AS v
WHERE v.Name = t.Property
AND v.Value = t.Value
)
)
SELECT [Count] = COUNT(t.Property),
[Column] = v.Name
FROM (SELECT DISTINCT Name FROM #V) AS V
LEFT JOIN InvalidProperties AS t
ON t.Property = v.Name
GROUP BY v.Name;
Which gives:
Count Column
--------------
0 TypeA
1 TypeB
1 TypeC
SCHEMA FOR ABOVE QUERIES
For SQL Server 2008+. Apologies, I don't have SQL Server 2005 anymore, and forgot it doesn't support table value constructors.
CREATE TABLE #T (Id INT, ParentId INT, Property VARCHAR(10), Value VARCHAR(10));
INSERT #T (Id, ParentId, Property, Value)
VALUES
(1, 1, 'Date', '01-09-2015'), (2, 1, 'CountValue', '2'), (3, 1, 'TypeA', 'Value1'),
(4, 1, 'TypeB', 'Value2'), (5, 1, 'TypeC', 'Value2'), (6, 2, 'Date', '15-10-2015'),
(7, 2, 'CountValue', '3'), (8, 2, 'TypeA', 'Value3'), (9, 2, 'TypeB', 'Value22'),
(10, 2, 'TypeC', 'Value99');
CREATE TABLE #V (ID INT, Name VARCHAR(5), Value VARCHAR(7));
INSERT #V (Id, Name, Value)
VALUES
(1, 'TypeA', 'Value1'), (2, 'TypeA', 'Value2'), (3, 'TypeA', 'Value3'),
(4, 'TypeB', 'Value20'), (5, 'TypeB', 'Value21'), (6, 'TypeB', 'Value22'),
(7, 'TypeC', 'Value1'), (8, 'TypeC', 'Value2');
Final result without PIVOT:
SELECT [count] = SUM(CASE WHEN l.id IS NULL THEN 1 ELSE 0 END)
,t.Property
FROM #lookup l
RIGHT JOIN #tab t
ON t.Property = l.Name
AND t.Value = l.Value
WHERE t.Property LIKE 'Type%'
GROUP BY t.Property;
LiveDemo
Data:
CREATE TABLE #tab(
Id INTEGER NOT NULL PRIMARY KEY
,ParentId INTEGER NOT NULL
,Property VARCHAR(10) NOT NULL
,Value VARCHAR(10) NOT NULL
);
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (1,1,'Date','01-09-2015');
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (2,1,'CountValue','2');
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (3,1,'TypeA','Value1');
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (4,1,'TypeB','Value2');
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (5,1,'TypeC','Value2');
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (6,2,'Date','15-10-2015');
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (7,2,'CountValue','3');
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (8,2,'TypeA','Value3');
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (9,2,'TypeB','Value22');
INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (10,2,'TypeC','Value99');
CREATE TABLE #lookup(
Id INTEGER NOT NULL PRIMARY KEY
,Name VARCHAR(5) NOT NULL
,Value VARCHAR(7) NOT NULL
);
INSERT INTO #lookup(Id,Name,Value) VALUES (1,'TypeA','Value1');
INSERT INTO #lookup(Id,Name,Value) VALUES (2,'TypeA','Value2');
INSERT INTO #lookup(Id,Name,Value) VALUES (3,'TypeA','Value3');
INSERT INTO #lookup(Id,Name,Value) VALUES (4,'TypeB','Value20');
INSERT INTO #lookup(Id,Name,Value) VALUES (5,'TypeB','Value21');
INSERT INTO #lookup(Id,Name,Value) VALUES (6,'TypeB','Value22');
INSERT INTO #lookup(Id,Name,Value) VALUES (7,'TypeC','Value1');
INSERT INTO #lookup(Id,Name,Value) VALUES (8,'TypeC','Value2');
EDIT:
Adding more criteria:
LiveDemo2
SELECT [count] = SUM(CASE WHEN l.id IS NULL THEN 1 ELSE 0 END)
,t.Property
FROM #lookup l
RIGHT JOIN #tab t
ON t.Property = l.Name
AND t.Value = l.Value
WHERE t.Property LIKE 'Type%'
AND t.ParentId IN (SELECT ParentId FROM #tab WHERE Property = 'Date' AND ISDATE(VALUE) = 1)
AND t.ParentID IN (SELECT ParentId FROM #tab WHERE Property = 'CountValue' AND Value > 0)
GROUP BY t.Property;

How to retrieve data with group by aggregate function in SQL Server 2012

I am new to SQL Server 2012. This is my table DDL & DML script.
CREATE TABLE [tbl_item_i18n]
(
[item_id] [int] NOT NULL,
[lang_id] [int] NOT NULL,
[item_text] [nvarchar](max) NULL
);
INSERT INTO [tbl_item_i18n] ([item_id],[lang_id],[item_text])
VALUES (1, 1, 'item1'), (1, 2, 'idem 1'),
(2, 1, 'item2'),
(3, 1, 'item3'), (3, 2, 'idem 3'),
(4, 1, 'item4'), (4, 2, 'idem 4');
My expected output is :
This is what I have tried :
select
lang_id,
case when lang_id = 2 AND itemI18N.item_text is not null then itemI18N.item_text
when lang_id = 1 then itemI18N.item_text
end as ite_texte
from
tbl_item_i18n itemI18N
group by
itemI18N.item_id, lang_id, itemI18N.item_text
But it does not give me expected result.
Purpose :- I would like to retrieve data for lang_id = 2. If the record for lang_id = 2 does not exist, then retrieve data for lang_id = 2.
How do I retrieve data using aggregate function?
LEFT JOIN Bring all the column with lenguaje 1, and null if doesnt have lenguaje 2.
I include extra column so you understand the result.
SELECT
item_id,
CASE
WHEN B.lang_id IS NULL THEN A.item_text
ELSE B.item_text
END as item_name,
A.*
B.*
FROM tbl_item_i18n A
LEFT JOIN tbl_item_i18n B
ON A.item_id = B.item_id
AND A.lang_id < B.lang_id
NOTE
Maybe need especial consideration if more than 2 lenguajes.
Another solution
SELECT *
FROM
(
SELECT item_id, lang_id, item_text as item_name,
ROW_NUMBER() over (partition by item_id order by lang_id desc) as RN
FROM tbl_item_i18n
) as t
WHERE RN = 1

sql puzzle sql server 2000

I have a small puzzle for myself.
PK is combination of lineNr and typeNr
i want to make a selection where all is_adres is N and the same for all linenrs, the same goes for is_postaddres. And I want the result in one view.
this is wat i want as end result
typenr,is_Adres ,is_postaddres
10, null , 'N'
11, 'N', null
13, 'N', 'N'
create table script + testdata
CREATE TABLE lineAdres
(
lineNr int,
typeNr int,
is_Adres char(1),
is_postaddres char(1)
);
INSERT INTO lineAdres VALUES
(1, 10,'J','N'),
(1, 11,'N','J'),
(1, 12,'N','J'),
(1, 13,'N','N'),
(2, 10,'J','N'),
(2, 11,'N','J'),
(2, 12,'J','N'),
(2, 13,'N','N');
I want to join this 2 views in one.. but how, and i got a feeling it could be more efficient!
select typenr, is_adres
from lineAdres
where is_adres = 'N'
group by typenr, is_adres
having count(*) = 2
select typenr, is_postaddres
from lineAdres
where is_postaddres = 'N'
group by typenr, is_postaddres
having count(*) = 2
To join the two result use a CTE and a full join to combine the data.
WITH IsAddress AS(
select typenr, is_adres
from lineAdres
where is_adres = 'N'
group by typenr, is_adres
having count(*) = 2),
IsPostAddress AS (select typenr, is_postaddres
from lineAdres
where is_postaddres = 'N'
group by typenr, is_postaddres
having count(*) = 2)
SELECT
COALESCE(IsAddress.typenr,IsPostAddress.typenr) typenr,
IsAddress.IsAddress,
IsPostAdress.is_postaddres
FROM
IsAddress
FULL OUTER JOIN
IsPostAdress
ON
IsAddress.typenr = IsPostAdress.typenr