Check Data Set for Null or Zeroes - sql

I have a table with 10 columns of floating values associated to a Serial Number.
Values can be entered or not in each column.
0 [zero] and NULL are not valid values.
Using SQL SERVER 2012.
My question is:
How can I check that given a specific serial number, none of these values is either 0 or NULL and return True if all values are present and False otherwise?
Please give me some suggestions.
Thank you

More than your effort deserved but I was curious. Cast to bit makes it a 1 or 0 and have to cast back to int to add.
declare #T table (int1 int, int2 int);
insert into #T values (null, null), (null, 0), (0, null), (null, 1), (10, null), (0, 0)
, (1, 0), (1, 10), (10, 0), (10, 10), (-2, -2);
with cte as
( select int1, cast(cast(isnull(int1, 0) as bit) as int) tf1
, int2, cast(cast(isnull(int2, 0) as bit) as int) tf2
from #T
)
select cte.int1, cte.int2
, iif(tf1 + tf2 = 2, 1, 0) as rr
from cte;

Related

SQL Server; How to incorporate unique values from STRING_AGG?

There are a handful of discussions on how to setup unique values with respect to STRING_AGG, see:
https://stackoverflow.com/a/51646573/6534818
https://stackoverflow.com/a/50589222/6534818
but I am struggling to port them over to my case as I would like to maintain my ordering of results as well as management of NULL.
Consider the following MWE -- how might I just return the distinct/unique values for the STRING_AGG operation?
CREATE TABLE #bridge (dim2Key int, groupKey int)
INSERT #bridge (dim2Key, groupKey)
VALUES (1, 1)
,(2, 1)
,(3, 1)
,(4, 2)
,(5, NULL)
CREATE TABLE #dim2 (dim2Key int, attributeOne varchar(255))
INSERT #dim2 (dim2Key, attributeOne)
VALUES (1, 'B')
,(2, 'A')
,(3, 'A')
,(4, NULL)
CREATE TABLE #dim1 (dim1Key int, attributeTwo int, attributeThree varchar(255))
INSERT #dim1 (dim1Key, attributeTwo, attributeThree)
VALUES (1, 35, 'val1')
,(2, 25, 'val2')
,(3, 45, 'val3')
,(4, 55, 'val1')
,(5, NULL, NULL)
CREATE TABLE #fact1 (dim1Key int, factvalue1 int, groupKey int)
INSERT #fact1 (dim1Key, factvalue1, groupKey)
VALUES (1, 5, 1)
,(2, 25, 1)
,(3, 55, 1)
,(4, 99, 2)
,(5, 99, NULL)
GO
SELECT
#fact1.dim1Key,
#fact1.factvalue1,
#fact1.groupKey,
#dim1.attributeTwo,
#dim1.attributeThree,
ISNULL(#dim2.attributeOne, '<missing>')
FROM #fact1
JOIN #dim1 ON #dim1.dim1key = #fact1.dim1key
CROSS APPLY (
SELECT attributeOne = STRING_AGG(ISNULL(#dim2.attributeOne, '<missing>'), ', ') WITHIN GROUP (ORDER BY #dim2.attributeOne)
FROM #bridge b
JOIN #dim2 ON #dim2.dim2key = b.dim2key
WHERE b.groupKey = #fact1.groupKey
) #dim2
Just put it in a subquery with DISTINCT
SELECT
#fact1.dim1Key,
#fact1.factvalue1,
#fact1.groupKey,
#dim1.attributeTwo,
#dim1.attributeThree,
ISNULL(#dim2.attributeOne, '<missing>')
FROM #fact1
JOIN #dim1 ON #dim1.dim1key = #fact1.dim1key
CROSS APPLY (
SELECT
attributeOne = STRING_AGG(ISNULL(d2.attributeOne, '<missing>'), ', ') WITHIN GROUP (ORDER BY d2.attributeOne)
FROM (
SELECT DISTINCT
#dim2.attributeOne
FROM #bridge b
JOIN #dim2 ON #dim2.dim2key = b.dim2key
WHERE b.groupKey = #fact1.groupKey
) d2
) #dim2

SQL Pivot Half of table

I have a table that consists of time information. It's basically:
Employee, Date, Seq, Time In, Time Out.
They can clock out multiple times a day, so I'm trying to get all of the clock outs in a day on one row. My result would be something like:
Employee, Date, TimeIn1, TimeOut1, TimeIn2, TimeOut2, TimeIn3, TimeOut3....
Where the 1, 2, and 3 are the sequence numbers. I know I could just do a bunch of left joins to the table itself based on employee=employee, date=date, and seq=seq+1, but is there a way to do it in a pivot? I don't want to pivot the employee and date fields, just the time in and time out.
The short answer is: Yes, it's possible.
The exact code will be updated if/when you provide sample data to clarify some points, but you can absolutely pivot the times out while leaving the employee/work date alone.
Sorry for the wall of code; none of the fiddle sites are working from my current computer
declare #test table (
pk int,
workdate date,
seq int,
tIN time,
tOUT time
)
insert into #test values
(1, '2020-11-25', 1, '08:00', null),
(1, '2020-11-25', 2, null, '11:00'),
(1, '2020-11-25', 3, '11:32', null),
(1, '2020-11-25', 4, null, '17:00'),
(2, '2020-11-25', 5, '08:00', null),
(2, '2020-11-25', 6, null, '09:00'),
(2, '2020-11-25', 7, '09:15', null),
-- new date
(1, '2020-11-27', 8, '08:00', null),
(1, '2020-11-27', 9, null, '08:22'),
(1, '2020-11-27', 10, '09:14', null),
(1, '2020-11-27', 11, null, '12:08'),
(1, '2020-11-27', 12, '01:08', null),
(1, '2020-11-27', 13, null, '14:40'),
(1, '2020-11-27', 14, '14:55', null),
(1, '2020-11-27', 15, null, '17:00')
select *
from (
/* this just sets the column header names and condenses their values */
select
pk,
workdate,
colName = case when tin is not null then 'TimeIn' + cast(empDaySEQ as varchar) else 'TimeOut' + cast(empDaySEQ as varchar) end,
colValue = coalesce(tin, tout)
from (
/* main query */
select
pk,
workdate,
/* grab what pair # this clock in or out is; reset by employee & date */
empDaySEQ = (row_number() over (partition by pk, workdate order by seq) / 2) + (row_number() over (partition by pk, workdate order by seq) % 2),
tin,
tout
from #test
) i
) a
PIVOT (
max(colValue)
for colName
IN ( /* replace w/ dynamic if you don't know upper boundary of max in/out pairs */
[TimeIn1],
[TimeOut1],
[TimeIn2],
[TimeOut2],
[TimeIn3],
[TimeOut3],
[TimeIn4],
[TimeOut4]
)
) mypivotTable
generates these results.
(I would provide a fiddle demo but they're not working for me today)

Select Id where column takes all values in

Please help me with an SQL query. Here go test tables with data:
CREATE TABLE "Cats"
(
"CatId" SERIAL PRIMARY KEY,
"Name" character varying NOT NULL
);
CREATE TABLE "Measures"
(
"MeasureId" SERIAL PRIMARY KEY,
"CatId" integer NOT NULL REFERENCES "Cats",
"Weight" double precision NOT NULL,
"MeasureDay" integer NOT NULL
);
INSERT INTO "Cats" ("Name") VALUES
('A'), ('B'), ('C')
;
INSERT INTO "Measures" ("CatId", "Weight", "MeasureDay") VALUES
(1, 5.0, 1),
(1, 5.3, 2),
(1, 6.1, 5),
(2, 3.2, 1),
(2, 3.5, 2),
(2, 3.8, 3),
(2, 4.0, 4),
(2, 4.0, 5),
(3, 6.6, 1),
(3, 6.9, 2),
(3, 7.0, 3),
(3, 6.9, 4)
;
How do I select those CatId that have measures for ALL 5 days (MeasureDay takes all values in (1, 2, 3, 4, 5)) ?
On this test data, the query should return 2 since only Cat with CatId = 2 has measures for all days (1, 2, 3, 4, 5).
I assume that I should use GROUP BY "CatId" and HAVING clauses, but what kind of query should be inside HAVING?
try like this using group by
select CatId
from Measures
where MeasureDay in (1, 2, 3, 4, 5)
group by CatId
having count(distinct MeasureDay) = 5;
You can use aggregation and a having clause:
select m.CatId
from measures m
group by m.CatId
having count(distinct measureDay) = 5;

How to group rows by their DATEDIFF?

I hope you can help me.
I need to display the records in HH_Solution_Audit table -- if 2 or more staffs enter the room within 10 minutes. Here are the requirements:
Display only the events that have a timestamp (LAST_UPDATED) interval of less than or equal to 10 minutes. Therefore, I must compare the current row to the next row and previous row to check if their DATEDIFF is less than or equal to 10 minutes. I’m done with this part.
Show only the records if the number of distinct STAFF_GUID inside the room for less than or equal to 10 minutes is at least 2.
HH_Solution_Audit Table Details:
ID - PK
STAFF_GUID - staff id
LAST_UPDATED - datetime when a staff enters a room
Here's what I got so far. This satisfies requirement # 1 only.
CREATE TABLE HH_Solution_Audit (
ID INT PRIMARY KEY,
STAFF_GUID NVARCHAR(1),
LAST_UPDATED DATETIME
)
GO
INSERT INTO HH_Solution_Audit VALUES (1, 'b', '2013-04-25 9:01')
INSERT INTO HH_Solution_Audit VALUES (2, 'b', '2013-04-25 9:04')
INSERT INTO HH_Solution_Audit VALUES (3, 'b', '2013-04-25 9:13')
INSERT INTO HH_Solution_Audit VALUES (4, 'a', '2013-04-25 10:15')
INSERT INTO HH_Solution_Audit VALUES (5, 'a', '2013-04-25 10:30')
INSERT INTO HH_Solution_Audit VALUES (6, 'a', '2013-04-25 10:33')
INSERT INTO HH_Solution_Audit VALUES (7, 'a', '2013-04-25 10:41')
INSERT INTO HH_Solution_Audit VALUES (8, 'a', '2013-04-25 11:02')
INSERT INTO HH_Solution_Audit VALUES (9, 'a', '2013-04-25 11:30')
INSERT INTO HH_Solution_Audit VALUES (10, 'a', '2013-04-25 11:45')
INSERT INTO HH_Solution_Audit VALUES (11, 'a', '2013-04-25 11:46')
INSERT INTO HH_Solution_Audit VALUES (12, 'a', '2013-04-25 11:51')
INSERT INTO HH_Solution_Audit VALUES (13, 'a', '2013-04-25 12:24')
INSERT INTO HH_Solution_Audit VALUES (14, 'b', '2013-04-25 12:27')
INSERT INTO HH_Solution_Audit VALUES (15, 'b', '2013-04-25 13:35')
DECLARE #numOfPeople INT = 2,
--minimum number of people that must be inside
--the room for #lengthOfStay minutes
#lengthOfStay INT = 10,
--number of minutes of stay
#dateFrom DATETIME = '04/25/2013 00:00',
#dateTo DATETIME = '04/25/2013 23:59';
WITH cteSource AS
(
SELECT ID, STAFF_GUID, LAST_UPDATED,
ROW_NUMBER() OVER (ORDER BY LAST_UPDATED) AS row_num
FROM HH_SOLUTION_AUDIT
WHERE LAST_UPDATED >= #dateFrom AND LAST_UPDATED <= #dateTo
)
SELECT [current].ID, [current].STAFF_GUID, [current].LAST_UPDATED
FROM
cteSource AS [current]
LEFT OUTER JOIN
cteSource AS [previous] ON [current].row_num = [previous].row_num + 1
LEFT OUTER JOIN
cteSource AS [next] ON [current].row_num = [next].row_num - 1
WHERE
DATEDIFF(MINUTE, [previous].LAST_UPDATED, [current].LAST_UPDATED)
<= #lengthOfStay
OR
DATEDIFF(MINUTE, [current].LAST_UPDATED, [next].LAST_UPDATED)
<= #lengthOfStay
ORDER BY [current].ID, [current].LAST_UPDATED
Running the query returns IDs:
1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 14
That satisfies requirement # 1 of having less than or equal to 10 minutes interval between the previous row, current row and next row.
Can you help me with the 2nd requirement? If it's applied, the returned IDs should only be:
13, 14
Here's an idea. You don't need ROW_NUMBER and previous and next records. You just need to queries unioned - one looking for everyone that have someone checked X minutes behind, and another looking for X minutes upfront. Each uses a correlated sub-query and COUNT(*) to find number of matching people. If number is greater then your #numOfPeople - that's it.
EDIT: new version: Instead of doing two queries with 10 minutes upfront and behind, we'll only check for 10 minutes behind - selecting those that match in cteLastOnes. After that will go in another part of query to search for those that actually exist within those 10 minutes. Ultimately again making union of them and the 'last ones'
WITH cteSource AS
(
SELECT ID, STAFF_GUID, LAST_UPDATED
FROM HH_SOLUTION_AUDIT
WHERE LAST_UPDATED >= #dateFrom AND LAST_UPDATED <= #dateTo
)
,cteLastOnes AS
(
SELECT * FROM cteSource c1
WHERE #numOfPeople -1 <= (SELECT COUNT(DISTINCT STAFF_GUID)
FROM cteSource c2
WHERE DATEADD(MI,#lengthOfStay,c2.LAST_UPDATED) > c1.LAST_UPDATED
AND C2.LAST_UPDATED <= C1.LAST_UPDATED
AND c1.STAFF_GUID <> c2.STAFF_GUID)
)
SELECT * FROM cteLastOnes
UNION
SELECT * FROM cteSource s
WHERE EXISTS (SELECT * FROM cteLastOnes l
WHERE DATEADD(MI,#lengthOfStay,s.LAST_UPDATED) > l.LAST_UPDATED
AND s.LAST_UPDATED <= l.LAST_UPDATED
AND s.STAFF_GUID <> l.STAFF_GUID)
SQLFiddle DEMO - new version
SQLFiddle DEMO - old version

Many to many cleanup

I have a many to many table that has become a bit of a mess and I'm looking to update the schema to prevent this happening in future. To do that first I need to clean up the existing data to use as the source of a merge script. Not knowing quite how to term this kind of query hitting Google hasn't really helped.
Broken down simply this is what I'm looking at and what I'm aiming to extract.
DECLARE #TestTable
TABLE ([IdA] INT, [IdB] INT);
DECLARE #Expected
TABLE ([KeyId] INT, [MemberId] INT);
INSERT INTO #TestTable
VALUES
(1, 2),
(2, 1),
(1, 3),
(4, 1),
(12, 4),
(5, 6),
(6, 7),
(8, 9),
(11, 10)
INSERT INTO #Expected
VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 12),
(5, 5),
(5, 6),
(5, 7),
(8, 8),
(8, 9),
(10, 10),
(10, 11)
Lowest Id of a group should be considered the key
Number of members of a group will always be less than 20
Ordering isn't important so long as the resultant dataset is correct
Many thanks.
Here's one way of achieving what you are trying to do:
Here's a SQLFiddle
--Let's copy the rows in as they are to begin with
INSERT INTO
#Expected
SELECT
[IdA]
,[IdB]
FROM
#TestTable
--Lets switch the column values where the higher ID's are in column B
UPDATE #Expected
SET [KeyId] = [MemberId], [MemberId] = [KeyId]
WHERE [KeyId] > [MemberId]
--Now lets set the KEY IDs
UPDATE
E
SET
E.[KeyId] = E1.[KeyId]
FROM
#Expected E
INNER JOIN
#Expected E1
ON
E1.[MemberId] = E.[KeyId]
--and you wanted mappings for the keys to themselves in your result set, so let's add those
INSERT INTO
#Expected
SELECT
[KeyId]
,[KeyId]
FROM
#Expected E
WHERE NOT EXISTS
(SELECT * FROM #Expected WHERE [KeyId] = E.[KeyId] AND [MemberId] = [KeyID])
SELECT DISTINCT
*
FROM
#Expected
Note that I'm selecting DISTINCT at the end because I haven't removed duplicates from the result set.