SQL Query to count number of records must match total number of records - sql

I have 2 tables as
Result Master
+------+-------------+
| QnID | Description |
+------+-------------+
| 1 | Qn1 |
| 2 | Qn2 |
| 3 | Qn3 |
| 4 | Qn4 |
| 5 | Qn5 |
+------+-------------+
Result Details
+----+------+--------+--------+
| ID | QnID | TCDesc | Result |
+----+------+--------+--------+
| 1 | 1 | TC1 | PASS |
| 2 | 1 | TC2 | FAIL |
| 3 | 1 | TC3 | PASS |
| 4 | 2 | TC1 | PASS |
| 5 | 3 | TC1 | PASS |
| 6 | 3 | TC1 | PASS |
| 7 | 3 | TC3 | PASS |
+----+------+--------+--------+
I need a query which will return following result:
+----+------+--------+
| ID | QnID | Result |
+----+------+--------+
| 1 | 2 | PASS |
| 2 | 3 | PASS |
| 3 | 4 | ERROR |
| 4 | 5 | ERROR |
+----+------+--------+
Conditions:
each question will have different number of testcase "ResultDetails", I need to select questions for which all the test case get passsed (number of entries for a particular question must be same as number of test cases passed for the same) or Error (ResultDetail doesn't have an entry for a question).
Can anyone please help me with a query, thank you.

You can get the desired results using a common table expression and conditional aggregation.
First, create and populate sample tables (Please save us this step in your future questions):
DECLARE #ResultMaster AS TABLE
(
QnID int,
Description char(3)
);
INSERT INTO #ResultMaster (QnID, Description) VALUES
(1, 'Qn1'),
(2, 'Qn2'),
(3, 'Qn3'),
(4, 'Qn4'),
(5, 'Qn5');
DECLARE #ResultDetails AS TABLE
(
ID int,
QnID int,
TCDesc char(3),
Result char(4)
);
INSERT INTO #ResultDetails VALUES
(1, 1, 'TC1', 'PASS'),
(2, 1, 'TC2', 'FAIL'),
(3, 1, 'TC3', 'PASS'),
(4, 2, 'TC1', 'PASS'),
(5, 3, 'TC1', 'PASS'),
(6, 3, 'TC1', 'PASS'),
(7, 3, 'TC3', 'PASS');
Then, use a common table expression to calculate the number of pass details and a simple count to get the number of total details:
WITH CTE AS
(
SELECT M.QnId,
COUNT(CASE WHEN Result = 'PASS' THEN 1 END) As CountPass,
COUNT(Result) As CountDetails
FROM #ResultMaster As M
LEFT JOIN #ResultDetails As D ON M.QnId = D.QnId
GROUP BY M.QnId
)
Then, select from that cte:
SELECT ROW_NUMBER() OVER(ORDER BY QnId) AS Id,
QnId,
CASE WHEN CountDetails = 0 THEN
'ERROR'
ELSE
'PASS'
END
FROM CTE
WHERE CountPass = CountDetails
Results:
+----+------+--------+
| ID | QnID | Result |
+----+------+--------+
| 1 | 2 | PASS |
| 2 | 3 | PASS |
| 3 | 4 | ERROR |
| 4 | 5 | ERROR |
+----+------+--------+
You can see a live demo on rextester.

Related

Generate multiple record from existing records based on interval columns [from and to]

I have 2 types of score [M,B] in column 3, if a type is M, then the score is either an S[scored] or SB[bonus scored] in column 6. Every interval [from_hrs - to_hrs] for a type B must have a corresponding SB for type M, thus, an interval for a type B cannot have a score of S for a type M. I have several records that were unfortunately captured as seen in the table below.
CREATE TABLE SCORE_TBL
(
ID int IDENTITY(1,1) PRIMARY KEY,
PERSONID_FK int NOT NULL,
S_TYPE varchar(50) NULL,
FROM_HRS int NULL,
TO_HRS int NULL,
SCORE varchar(50) NULL,
);
INSERT INTO SCORE_TBL(PERSONID_FK,S_TYPE,FROM_HRS,TO_HRS,SCORE)
VALUES
(1, 'M' , 0,20, 'S'),
(1, 'B',6, 8, 'B'),
(2, 'B',0, 2, 'B'),
(2, 'M',0,20, 'S'),
(2, 'B', 10,13, 'B'),
(2, 'B', 18,20, 'B'),
(2, 'M', 13,18, 'S');
| ID | PERSONID_FK |S_TYPE| FROM_HRS | TO_HRS | SCORE |
|----|-------------|------|----------|--------|-------|
| 1 | 1 | M | 0 | 20 | S |
| 2 | 1 | B | 6 | 8 | B |
| 3 | 2 | B | 0 | 2 | B |
| 4 | 2 | M | 0 | 20 | S |
| 5 | 2 | B | 10 | 13 | B |
| 6 | 2 | B | 18 | 20 | B |
| 7 | 2 | M | 13 | 18 | S |
I want the data to look like this
| ID | PERSONID_FK |S_TYPE| FROM_HRS | TO_HRS | SCORE |
|----|-------------|------|----------|--------|-------|
| 1 | 1 | M | 0 | 6 | S |
| 2 | 1 | M | 6 | 8 | SB |
| 3 | 1 | B | 6 | 8 | B |
| 4 | 1 | M | 8 | 20 | S |
| 5 | 2 | B | 0 | 2 | B |
| 6 | 2 | M | 0 | 2 | SB |
| 7 | 2 | M | 2 | 10 | S |
| 8 | 2 | B | 10 | 13 | B |
| 9 | 2 | M | 10 | 13 | SB |
| 10 | 2 | M | 13 | 18 | S |
| 11 | 2 | B | 18 | 20 | B |
| 12 | 2 | S | 18 | 20 | SB |
Any ideas on how to generate this data in SQL Server select statement? Visually, this what am trying to get.
Tricky part here is that interval might need to be split in several pieces like 0..20 for person 2.
Window functions to the rescue. This query illustrates what you need to do:
WITH
deltas AS (
SELECT personid_fk, hrs, sum(delta_s) as delta_s, sum(delta_b) as delta_b
FROM (SELECT personid_fk, from_hrs as hrs,
case when score = 'S' then 1 else 0 end as delta_s,
case when score = 'B' then 1 else 0 end as delta_b
FROM score_tbl
UNION ALL
SELECT personid_fk, to_hrs as hrs,
case when score = 'S' then -1 else 0 end as delta_s,
case when score = 'B' then -1 else 0 end as delta_b
FROM score_tbl) _
GROUP BY personid_fk, hrs
),
running AS (
SELECT personid_fk, hrs as from_hrs,
lead(hrs) over (partition by personid_fk order by hrs) as to_hrs,
sum(delta_s) over (partition by personid_fk order by hrs) running_s,
sum(delta_b) over (partition by personid_fk order by hrs) running_b
FROM deltas
)
SELECT personid_fk, 'M' as s_type, from_hrs, to_hrs,
case when running_b > 0 then 'SB' else 'S' end as score
FROM running
WHERE running_s > 0
UNION ALL
SELECT personid_fk, s_type, from_hrs, to_hrs, score
FROM score_tbl
WHERE s_type = 'B'
ORDER BY personid_fk, from_hrs;
Step by step:
deltas is union of two passes on score_tbl - one for start and one for end of score/bonus interval, creating a timeline of +1/-1 events
running calculates running total of deltas over time, yielding split intervals where score/bonus are active
final query just converts score codes and unions bonus intervals (which are passed unchanged)
SQL Fiddle here.

Kudu Conditional UPSERT INTO

Does Kudu support conditions on the UPDATE portion of UPSERT INTO?
Can I provide a conditional clause to only update given values based on a comparison between the insert values and destination table?
The actual use case is to update a timestamp column with the latest.
Here's the behavior as I imagine it.
CREATE TABLE my_first_table
(
id INT,
name STRING,
status INT,
PRIMARY KEY(id)
)
PARTITION BY HASH PARTITIONS 4
STORED AS KUDU;
INSERT INTO my_first_table VALUES (1, "lee", 101), (2 "shiv", 102), (3,"bob", 103);
--CONDITION FALSE, UPDATE NOT PERFORMED
UPSERT INTO my_first_table AS t
VALUES (3, "bobby", 100) AS v
WHERE v.status > t.status
+----+------+--------+
| id | name | status |
+----+------+--------+
| 1 | lee | 101 |
| 2 | shiv | 102 |
| 3 | bob | 103 |
+----+------+--------+
--CONDITION TRUE, UPDATE PERFORMED
UPSERT INTO my_first_table AS t
VALUES (3, "bobby", 100) AS v
WHERE v.status < t.status
+----+------+--------+
| id | name | status |
+----+------+--------+
| 1 | lee | 101 |
| 2 | shiv | 102 |
| 3 | bobby| 100 |
+----+------+--------+
In the case where 3 does not exist, it should insert.
Is there an elegant workaround if not?
A solution I found was to use a LEFT JOIN and filter in the SELECT expression. So say we have an table to_upsert identical to the destination table with all our potential upserts...
INSERT INTO to_upsert VALUES (3, "bobby" 100), (5, "newgal", 600);
UPSERT INTO my_first_table
SELECT to_upsert.id, to_upsert.name, to_upsert.status
FROM to_upsert
LEFT JOIN my_first_table ON to_upsert.id = my_first_table.id
WHERE my_first_table.status > to_upsert.status OR my_first_table.id IS NULL;
SELECT * FROM my_first_table;
+----+--------+--------+
| id | name | status |
+----+--------+--------+
| 3 | bobby | 100 |
| 1 | lee | 101 |
| 2 | shiv | 102 |
| 5 | newgal | 600 |
+----+--------+--------+
Thank you for watching this episode of watching me learn sql.

Find the period of occurence of a value in table

I have a table with the following data.
+------------+---------+
| Date | Version |
+------------+---------+
| 1/10/2019 | 1 |
| .... | |
| 15/10/2019 | 1 |
| 16/10/2019 | 2 |
| .... | |
| 26/10/2019 | 2 |
| 27/10/2019 | 1 |
| .... | |
| 30/10/2019 | 1 |
+------------+---------+
I need to find the period of occurrence for version in the table.
Eg:Suppose I need to get Version 1 occurence details which is present from 1/10/2019 to 15/10/2019 and from 27/10/2019 to 30/10/2019. How can i query the database for such a result?
I have tried many ways but not able to produce the desired result .I even doubt this is possible using a query!
Any inputs are highly appreciated.
Expected output:
+---------+-------------+-------------+
| Version | Period from | Period To |
+---------+-------------+-------------+
| 1 | 1/10/2019 | 15/10/2019 |
| 2 | 16/10/2019 | 26/10/2019 |
| 1 | 27/10/2019 | 30/10/2019 |
+---------+-------------+-------------+
This is gaps and Islands question.
Try this
DECLARE #SampleData TABLE ( [Date] DATE, [Version] INT)
INSERT INTO #SampleData ([Date], [Version])
VALUES
('01-10-2019', 1), ('02-10-2019', 1), ('15-10-2019', 1),
('16-10-2019', 2), ('17-10-2019', 2),('26-10-2019', 2),
('27-10-2019', 1), ('28-10-2019', 1), ('30-10-2019', 1)
SELECT
Y.[Version]
,PeriodFrom = MIN(Y.[Date])
,PeriodTo = MAX(Y.[Date])
FROM(
SELECT
X.[Version]
,X.[Date]
,ISLAND = RN-ROW_NUMBER()OVER( PARTITION BY X.[Version] ORDER BY X.[Date])
FROM(
SELECT
RN=ROW_NUMBER()OVER( ORDER BY S.[Date])
,S.[Date]
,S.[Version]
FROM
#SampleData S
) X
) Y
GROUP BY
Y.[Version], Y.ISLAND
ORDER BY
PeriodFrom
Output
Version PeriodFrom PeriodTo
1 2019-10-01 2019-10-15
2 2019-10-16 2019-10-26
1 2019-10-27 2019-10-30

PostgreSQL Current count of specific value

I need to achieve a view such as:
+------------+----------+--------------+----------------+------------------+
| Parent id | Expected | Parent Value | Distinct Value | Distinct Value 2 |
+------------+----------+--------------+----------------+------------------+
| 1 | 001.001 | 3 | 6/1/2017 | 5,000.00 |
| 1 | 001.002 | 3 | 9/1/2018 | 3,500.00 |
| 1 | 001.003 | 3 | 1/7/2018 | 9,000.00 |
| 2 | 002.001 | 7 | 9/1/2017 | 2,500.00 |
| 3 | 003.001 | 5 | 3/6/2017 | 1,200.00 |
| 3 | 003.002 | 5 | 16/8/2017 | 8,700.00 |
+------------+----------+--------------+----------------+------------------+
where I get distinct child objects that have same parents, but I cannot make the "Expected" column work. Those zeros don't really matter, I just need to get subindex like "1.1", "1.2" to work. I tried rank() function but it seems it doesnt really help.
Any help appreciated, thanks in advance.
My initial try looks like this:
SELECT DISTINCT
parent.parent_id,
rank() OVER ( order by parent_id ) as expected,
parent.parent_value,
ct.distinct_value,
ct.distinct_value_2
FROM parent
LEFT JOIN (crosstab (...) )
AS ct( ... )
ON ...
Use partition by parent_id in the window function and order by another_col to define the order in groups by parent_id.
with parent(parent_id, another_col) as (
values (1, 30), (1, 20), (1, 10), (2, 40), (3, 60), (3, 50)
)
select
parent_id,
another_col,
format('%s.%s', parent_id, row_number() over w) as expected
from parent
window w as (partition by parent_id order by another_col);
parent_id | another_col | expected
-----------+-------------+----------
1 | 10 | 1.1
1 | 20 | 1.2
1 | 30 | 1.3
2 | 40 | 2.1
3 | 50 | 3.1
3 | 60 | 3.2
(6 rows)

How to use joins and sum() in SQL Server query

I have two tables Items and Transactions. In the items table, all the items are listed. In the transactions table it is where a particular employee can request for an item depending on the quantity that he/she requested.
How to use joins to merge the data from two tables that will compute for the balance quantity of each item?
Note: (Quantity Balance = Quantity - TR_Qty)
ITEMS table:
| ID | ITEM | UNIT | QUANTITY | PRICE |
| 1 | Perfume | btl. | 50 | 200.00 |
| 2 | Battery | pc. | 100 | 25.00 |
| 3 | Milk | can | 250 | 70.00 |
| 4 | Soap | pack | 400 | 150.00 |
TRANSACTIONS table:
| ID | ITEM_ID | TR_QTY | REQUSETOR | PROCESSOR | Date |Time |
| 1 | 1 | 20 | A. Jordan | K. Koslav | 12-22-2014 |09:00|
| 2 | 2 | 8 | B. Wilkins | Z. Flores | 12-22-2014 |10:03|
| 3 | 3 | 80 | C. Potran | A. Mabag | 12-26-2014 |14:23|
| 4 | 3 | 45 | D. Korvak | D. Sanchez | 12-28-2014 |15:33|
| 5 | 4 | 22 | C. Carvicci | A. Flux | 12-31-2014 |16:02|
| 6 | 1 | 18 | F. Sansi | N. Mahone | 01-22-2015 |08:45|
| 7 | 4 | 14 | Z. Gorai | M. Sucre | 01-30-2015 |16:33|
| 8 | 2 | 7 | L. ZOnsey | P. Panchito | 02-11-2015 |17:22|
Desired output:
| ID | ITEM | QUANITY BALANCE|
| 1 | Perfume | 462 |
| 2 | Battery | 85 |
| 3 | Milk | 125 |
| 4 |Soap | 364 |
Try this:
DECLARE #Items TABLE(ID INT, Item NVARCHAR(10), Q INT)
DECLARE #Transactions TABLE(ID INT, ItemID INT, TQ INT)
INSERT INTO #Items VALUES
(1, 'Perfume', 500),
(2, 'Battery', 100),
(3, 'Milk', 250),
(4, 'Soap', 400)
INSERT INTO #Transactions VALUES
(1, 1, 20),
(2, 2, 8),
(3, 3, 80),
(4, 3, 45),
(5, 4, 22),
(6, 1, 18),
(7, 4, 14),
(8, 2, 7)
SELECT i.ID, i.Item, MAX(i.Q) - ISNULL(SUM(t.TQ), 0) AS Balance
FROM #Items i
LEFT JOIN #Transactions t ON t.ItemID = i.ID
GROUP BY i.ID, i.Item
ORDER BY i.ID
Output:
ID Item Balance
1 Perfume 462
2 Battery 85
3 Milk 125
4 Soap 364
You can do it for example by using outer apply and creating the sum of quantities in there:
select
I.ID,
I.ITEM,
I.QUANTITY - isnull(T.QUANTITY, 0) as BALANCE
from
ITEMS I
outer apply (
select sum(TR_QTY) as QUANTITY
from TRANSACTIONS T
where T.ITEM_ID = I.ID
) T
SELECT ITEM , ( SELECT (SUM(TRANSACTIONS.TR_QTY)-ITEMS.TR_QTY) FROM TRANSACTIONS WHERE TRANSACTIONS.ITEM_ID = ITEMS.ID ) AS QUANITY BALANCE FROM ITEMS
Field name and table name is as you mentioned in query ( you should change that as space is not valid for field or table name )