DB2 SQL Result Set without Duplicates - sql

I am trying to adjust this SQL syntax to only show one row if it has a dash number in the field or field is empty.
Select Distinct TRIM(TRANSLATE(itnbr,' ','F')),
Case When t3.dashonly Is NULL Then '' Else t3.dashonly End As dashonly
From amflib1.itmrva t1
Join webprddt1.drawmext17 t2 On t2.afctdwg = t1.uu25
Left Join webprddt1.wqmssoadn t3 On t3.itemno = t1.itnbr
Where t2.recseq = '0060' Order By 1
As is the resultset is:
00001 DASHONLY
--------------- ---------------
41031052-1
41031052-1 -1
41031052-10
41031052-11 -11
41031052-11
41031052-12
41031052-12 -12
41031052-13
41031052-14
41031052-15
41031052-17
Desired resultset:
00001 DASHONLY
--------------- ---------------
41031052-1 -1
41031052-10
41031052-11 -11
41031052-12 -12
41031052-13
41031052-14
41031052-15
41031052-17

Thought I don't have test data to test your query, the query below should work.
You can use ROW_NUMBER() to sort the rows in each subgroup according to a sorting criteria, and then just pick the first one per group.
select *
from (
select
TRIM(TRANSLATE(itnbr,' ','F')),
case when t3.dashonly is null then '' else t3.dashonly end As dashonly,
row_number() over(partition by TRIM(TRANSLATE(itnbr,' ','F'))
order by case when t3.dashonly is null then 0 else 1 end) as rn
from amflib1.itmrva t1
join webprddt1.drawmext17 t2 on t2.afctdwg = t1.uu25
left join webprddt1.wqmssoadn t3 on t3.itemno = t1.itnbr
where t2.recseq = '0060'
) x
where rn = 1
order by 1

Related

Get single row depending of conditional

I have a simple select query with some joins like:
SELECT
[c].[column1]
, [c].[column2]
FROM [Customer] AS [c]
INNER JOIN ...
So I do a left join with my principal table as:
LEFT JOIN [Communication] AS [com] ON [c].[CustomerGuid] = [com].[ComGuid]
this relatioship its 1 to *, one customer can have multiple communications
So in my select I want to get value 1 or 2 depending of condition:
Condition:
if ComTypeKey (from communication) table have a row with value 3 and have another row with vale 4 return 1 then 0
So I try something like:
SELECT
[c].[column1]
, [c].[column2]
, IIF([com].[ComTypeKey] = 3 AND [com].[ComTypeKey] = 4,1,0)
FROM [Customer] AS [c]
INNER JOIN ...
LEFT JOIN [Communication] AS [com] ON [c].[CustomerGuid] = [com].[ComGuid]
But it throws me two rows, beacause there are 2 rows on communication. My desire value is to get only one row with value 1 if my condition is true
If you have multiple rows you need GROUP BY, then count the relevant keys and subtract 1 to get (1, 0)
SELECT
[c].[column1]
, [c].[column2]
, COUNT(CASE WHEN [ComTypeKey] IN (3,4) THEN 1 END) - 1 as FLAG_CONDITION
FROM [Customer] AS [c]
INNER JOIN ...
LEFT JOIN [Communication] AS [com]
ON [c].[CustomerGuid] = [com].[ComGuid]
GROUP BY
[c].[column1]
, [c].[column2]
I'm not really sure I understand.
This will literally find if both values 3 and 4 exist for that CustomerGuid, and only select one of them in that case - not filtering out any record otherwise.
If this is not what you want, providing sample data with the expected result would remove the ambiguity.
SELECT Field1,
Field2,
...
FieldN
FROM (SELECT TMP.*,
CASE WHEN hasBothValues = 1 THEN
ROW_NUMBER() OVER ( PARTITION BY CustomerGuid ORDER BY 1 )
ELSE 1
END AS iterim_rn
FROM (SELECT TD.*,
MAX(CASE WHEN Value1 = '3' THEN 1 ELSE 0 END) OVER
( PARTITION BY CustomerGuid ) *
MAX(CASE WHEN Value1 = '4' THEN 1 ELSE 0 END) OVER
( PARTITION BY CustomerGuid ) AS hasBothValues
FROM TEST_DATA TD
) TMP
) TMP2
WHERE interim_rn = 1

Group By + Left Outer Join In Sqlite

I'm trying to join 2 tables (even if there's no match for the 2nd table I want to bring the results).
So I thought I can solve that problem using a LEFT OUTER JOIN, but for some reason I'm not able to do that.
Here's the schema:
entry_types table:
ID NAME
---------- ----------
1 entry_type1
2 entry_type2
entries table:
ID VALUE ENTRY_TYPE_ID DATE
---------- ---------- -------------- ----------
1 55.5 1 2016-09-18T17:46:27.398Z
2 84.21 2 2016-09-18T18:41:54.142Z
3 144.5 2 2016-09-19T01:13:51.099Z
4 150.7 1 2016-07-17T19:28:12.026Z
Looking to the schema above we can imply that I have both entry_types ocurring in September, but in July I have only one entry_type.
So, what I want?
I want to retrieve always the two entry_types, and, of course, set 0 to the inexistent entry_type (if there's one).
The following query that I'm trying is the following:
SELECT et.name as entry_type,
SUM(CASE WHEN en.value IS NULL THEN 0 ELSE en.value END) as total
FROM entries en
LEFT OUTER JOIN entry_types et
ON en.entry_type_id = et.id
WHERE STRFTIME('%m', en.date) = 'SOME MONTH'
GROUP BY en.entry_type_id
The expected result:
If I search by 'September':
NAME TOTAL
---------- ----------
entry_type1 55.5
entry_type2 228.71
If I search by 'July':
NAME TOTAL
---------- ----------
entry_type1 150.7
entry_type2 0
Thanks in advance. Any help will be appreciated.
SELECT t1.name,
COALESCE(t2.value, 0)
FROM entry_types t1
LEFT JOIN
(
SELECT entry_type_id, SUM(value) AS value
FROM entries
WHERE STRFTIME('%m', date) = 'SOME MONTH'
GROUP BY entry_type_id
) t2
ON t1.id = t2.entry_type_id
I think we can do that without using sub query
SELECT et.id,et.name AS entry_type,
CASE WHEN IFNULL(SUM(en.value), '') = '' THEN 0 ELSE SUM(en.value) AS total
FROM entry_types et
LEFT JOIN entries en ON en.entry_type_id = et.id
AND STRFTIME('%m', en.date) = 'SOME MONTH'
GROUP BY et.id,et.name

Returning only id's of records that meet criteria

I need to return distinct ID's of records which meet following conditions :
must have records with field reason_of_creation = 1
and must NOT have records with field reason_of_creation = 0 or null
in the same time.
While i was able to do it, i keep wondering is there more elegant (even recommended) way of doing it.
Here is anonymized version of what i have :
select distinct st.some_id from (
select st.some_id, wanted.wanted_count as wanted, unwanted.unwanted_count as unwanted
from some_table st
left join (
select st.some_id, count(st.reason_of_creation) as wanted_count
from some_table st
where st.reason_of_creation=1
group by st.some_id
) wanted on wanted.some_id = st.some_id
left join (
select st.some_id, count(st.reason_of_creation) as unwanted_count
from some_table st
where st.reason_of_creation=0
group by st.some_id
) unwanted on unwanted.some_id = st.some_id
where wanted.wanted_count >0 and (unwanted.unwanted_count = 0 or unwanted.unwanted_count is null)
) st;
Sample data :
some_id reason_of_creation
1 1
1 0
2 1
3 null
4 0
4 1
5 1
desired result would be list of records with some_id = 2, 5
It seems to me your query is overkill,all you need is some post aggregation filtering
SELECT some_id FROM t
GROUP BY some_id
HAVING SUM(CASE WHEN reason_of_creation = 1 THEN 1 ELSE 0 END)>0
AND SUM(CASE WHEN reason_of_creation = 0 OR reason_of_creation IS NULL THEN 1 ELSE 0 END)=0
I think that more elegant query exists and it is based on assumption what reasoson_of_crdeation field is integer, so minimal possible it's value, which greater than 0 is 1
This is for possible negative values for reasoson_of_crdeation:
select someid from st
where reasoson_of_crdeation != -1
group by someid
having(min(nvl(abs(reasoson_of_crdeation), 0)) = 1)
or
select someid from st
group by someid
having(min(nvl(abs(case when reasoson_of_crdeation = -1 then -2 else reasoson_of_crdeation end), 0)) = 1)
And this one in a case if reasoson_of_crdeation is non-negative integer:
select someid from st
group by someid
having(min(nvl(reasoson_of_crdeation, 0)) = 1)

Query to find ranges of consecutive rows

I have file that contains a dump of a SQL table with 2 columns: int ID (auto increment identity field) and bit Flag. The flag = 0 means a record is good and the flag = 1 means a record is bad (contains an error). The goal is to find all blocks of consecutive bad records (with flag value of 1) with 1,000 or more rows. The solution shouldn't use cursors or while loops and it should use the set-based queries only (selects, joins etc).
We would like to see the actual queries used and the results in the following format:
StartID – EndID NumberOfErrorsInTheBlock
StartID – EndID NumberOfErrorsInTheBlock
……………………….
StartID – EndID NumberOfErrorsInTheBlock
For example if our data were only 30 records and we were looking for blocks with 5 or more records then the results would look as follows (see the screenshot below, the errors blocks that met the criteria are highlighted) :
[ID Range].....[Number of errors in the block]
11-15..... 5
19-25..... 7
sql file containing sample rows, dropbox
T-SQL Solution for SQL Server 2012 and Above
IF OBJECT_ID('tempdb..#tbl_ranges') IS NOT NULL
DROP TABLE #tbl_ranges;
CREATE TABLE #tbl_ranges
(
row_num INT PRIMARY KEY,
ID INT,
Flag BIT,
Label TINYINT
);
WITH cte_yourTable
AS
(
SELECT Id,
Flag,
CASE
--label min
WHEN Flag != LAG(flag,1) OVER (ORDER BY ID) THEN 1
--inner
WHEN Flag = LAG(flag,1) OVER (ORDER BY ID) AND Flag = LEAD(flag,1) OVER (ORDER BY ID) THEN 2
--end
WHEN Flag = LAG(flag,1) OVER (ORDER BY ID) AND Flag != LEAD(flag,1) OVER (ORDER BY ID) THEN 3
END label
FROM yourTable
)
INSERT INTO #tbl_ranges
SELECT ROW_NUMBER() OVER (ORDER BY ID) row_num,
ID,
Flag,
label
FROM cte_yourTable
WHERE label != 2;
SELECT A.ID ID_start,
B.ID ID_end,
B.ID - A.ID range_cnt
FROM #tbl_ranges A
INNER JOIN #tbl_ranges B
ON A.row_num = B.row_num - 1
AND A.Flag = B.Flag;
IF OBJECT_ID('tempdb..#tbl_ranges') IS NOT NULL
DROP TABLE #tbl_ranges;
Abbreviated Results:
ID_start ID_end range_cnt
----------- ----------- -----------
2 3 1
5 8 3
9 10 1
11 35 24
36 356 320
357 358 1
359 406 47
...
With out using Temp Table, This is the best solution, Here is the Answer and It is perfect example for CTE with in CTE ( Nested CTE )
With Evaluation (ID,Flag,Evaluate)
as
(select ID,Flag,Evaluate = ID-row_number() over (order by Flag,ID)
from [dbo].[SqltestRecordsNew]
where Flag = 1
),
Evaluation_Final (StartingRecordID,EndRecordID,Flag,cnt)
as
(
select min(ID) as StartingRecordID,max(ID) as EndRecordID,
Flag, cnt = count(*)
from Evaluation
group by Evaluate, Flag
)
select Concat(StartingRecordID,' - ', EndRecordID) as 'StartingRecordID - EndRecordId',
cnt as GroupItemCnt from Evaluation_Final
where cnt > 999
order by Concat(StartingRecordID,' - ', EndRecordID)
-- Test results Case 1
Select ID,Flag,
Case when Flag=1 then 'Success'
else 'Defect Data'
End as TestResults
from SqltestRecordsNew
where ID between 1494363 and 1495559
-- Test results Case 2
Select ID,Flag,
Case when Flag=1 then 'Success'
else 'Defect Data'
End as TestResults from SqltestRecordsNew
where ID between 1498409 and 1503899
-- Test results Case 3
Select ID,Flag,
Case when Flag=1 then 'Success'
else 'Defect Data'
End as TestResults from SqltestRecordsNew
where ID between 1548257 and 1550489

SQL: selecting unique values based on conditions

I have a table containing 5 columns. The first column contains an ID, two columns contain parameters for those IDs with the values 0 or 1, a third column contains a parameter which I need as output, the last column contains a date. The same ID can appear in several rows with different parameters:
ID parameter1 parameter2 parameter3 date
001 0 1 A 01.01.2010
001 0 1 B 02.01.2010
001 1 0 C 01.01.2010
001 1 1 D 01.01.2010
002 0 1 A 01.01.2010
For each unique ID I want to return the value in parameter3, the decision from which row to return this value is based on the values in parameter1 and parameter2 and the date:
If there is a row with both parameters being 0, I want the value in this row.
If there is no such row, I want the value from the row where parameter1 is 0 and parameter2 is 1,
If there is no such row, I want the row where parameter1 is 1 and parameter2 is 0.
Finally, if there is no such row, I want the value from the row with both parameters being 1.
If there is more than one row matching the required conditions, I want the row with the most recent date.
e.g., for the table above, for the ID 001 I would want the second row with the value B in parameter3.
What would be the most effective / fastest way to accomplish this? I tried two approaches so far:
the first one was to select all distinct IDs and then loop through the distinct IDs, using a select statement with the ID in the where clause and then loop through all the rows matching the ID while storing the necessary values in variables.:
foreach
select distinct ID into i_ID from table1
foreach
let o_case = 5
select case
when parameter1 = 0 and parameter2 = 0 then 1
when parameter1 = 0 and parameter2 = 1 then 2
when parameter1 = 1 and parameter2 = 0 then 3
when parameter1 = 1 and parameter2 = 1 then 4
end, parameter3, date
into i_case, i_p3, i_date
from table3
where table3.ID = i_ID
if i_case < o_case
then let o_p3, o_case, o_date = i_p3, i_case, i_date;
else ( if i_case = o_case and i_date > o_date
then let o_p3, o_date = i_p3, i_date;
end if;
end if;
end foreach;
insert into table_output values(i_ID; o_p3);
end foreach;
The second approach was to left join the table four times with itself on the ID and apply the different combinations of the parameter1 & parameter2 as described above in the left joins, then selecting the output via nested nvl clauses:
select ID,
nvl(t1.parameter3,
nvl(t2.parameter3,
nvl(t3.parameter3,
nvl(t4.parameter3)))) parameter3
from table1 t0
left join table1 t1
on t0.ID = t1.ID and t1.parameter1 = 0 and t1.parameter2 = 0
and t1.date = (select max(date) from table1 t1a where t1a.ID = t1.ID)
left join table1 t2
on t0.ID = t2.ID and t2.parameter1 = 0 and t2.parameter2 = 1
and t2.date = (select max(date) from table1 t2a where t2a.ID = t1.ID)
left join table1 t3
on t0.ID = t3.ID and t3.parameter1 = 1 and t3.parameter2 = 0
and t3.date = (select max(date) from table1 t3a where t3a.ID = t3.ID)
left join table1 t4
on t0.ID = t4.ID and t4.parameter1 = 1 and t4.parameter2 = 1
and t4.date = (select max(date) from table1 t4a where t4a.ID = t4.ID)
Both approaches basically worked, however, as the table is really long, they were much too slow. What would you recommend?
PS: DBMS is IBM Informix 10, this unfortunately restricts the range of available functions a lot.
I'm not sure if this is what you wanted, but this could work:
SELECT id, parameter3
FROM (
SELECT id, parameter3, RANK() OVER (
PARTITION BY id, parameter3
ORDER BY parameter1 ASC, parameter2 ASC, date DESC
)
FROM tab
) AS x
WHERE x.rank = 1;
ID parameter1 parameter2 parameter3 date
001 0 1 A 01.01.2010
001 0 1 B 02.01.2010
both of the above rows having same ID, paramaeter1, parameter2 but different paraameter3, it can create trouble for you.