Selecting rows - unique field as criteria - sql

Having this table:
Row Pos Outdata Mismatch Other
1 10 S 0 A
2 10 S 5 A
3 10 R 0 B
4 10 R 7 B
5 20 24 0 A
6 20 24 5 B
6 20 32 10 C
How can I select all rows where Pos=10 having unique Outdata. If more than one row exists, I would like to have the row where the field Mismatch is smallest. Ie I would like to get rows 1 and 3, not 2 and 4.
In that select I would also like to do the same for all Pos=20, so the total result should be rows 1,3,5,6
(And I want to then access the "Other" field, so I cant only SELECT DISTINCT on Pos and OutData and Mismatch).
Is there a query to do this in MySQL?

Here I am assuming that (Pos, OutData, Mismatch) is not unique, but that (Row, Pos, OutData, Mismatch) is unique:
SELECT T3.*
FROM Codes T3
JOIN (
SELECT MIN(Row) AS Row
FROM (
SELECT Pos, OutData, Min(Mismatch) AS Mismatch
FROM Codes
GROUP BY Pos, OutData
) T1
JOIN Codes T2
ON T1.Pos = T2.Pos AND T2.OutData = T2.Outdata AND T1.Mismatch = T2.Mismatch
GROUP BY T2.Pos, T2.OutData, T2.Mismatch
) T4
ON T3.Row = T4.Row
Result:
1, 10, 'S', 0, 'A'
3, 10, 'R', 0, 'B'
5, 20, '24', 0, 'A'
7, 20, '32', 10, 'C'
Note that I have also corrected the second row 6 to become row 7, as I believe that this was a mistake in the question.

Rationale is to create a table with all values of Pos, OutData and the lowest Mismatch and use the combination of these fields as a unique key into your actual table.
SELECT t1.*
FROM MyTable t1
INNER JOIN (
SELECT Pos, OutData, Mismatch = MIN(Mismatch)
FROM MyTable
GROUP BY Pos, OutData
) t2 ON t2.Pos = t1.Pos
AND t2.OutData = t1.OutData
AND t2.Mismatch = t1.Mismatch

Try this:
Select * From Table ot
Where pos = 10
And MisMatch =
(Select Min(MisMatch) From Table
Where pos = 10
And Outdata = ot.OutData)

This should work for you:
SELECT *
FROM table T1
GROUP BY Pos, Outdata
HAVING Mismatch = (
SELECT MIN(Mismatch)
FROM table T2
WHERE Pos = T1.Pos AND
Outdata = T1.Outdata
)

Related

SQL Server loop through a table for every 5 rows

I need to write a stored procedure or table function to return a new data table as a new data source.
I wish to loop through the original table for every 5 rows base on the invoice ID column (it's possible not start from 1), the first 5 rows add to the left of the new table and the second 5 rows add to the right of the new table, the third 5 rows to the left and so on.
For example, Here is the original table:
Here is the expect table:
Thanks in advance!
declare #rowCount int = 5;
with cte as (
select *,( (IN_InvoiceID-1) / #rowCount ) % 2 group1
,( (IN_InvoiceID-1) / #rowCount ) group2
,IN_InvoiceID % #rowCount group3
from T
)
select * from cte
select T1.INID,T1.IN_InvoiceID,T1.IN_InvoiceAmount,T2.INID,T2.IN_InvoiceID,T2.IN_InvoiceAmount
from CTE T1
left join CTE T2 on T2.group1 = 1 and T1.group2 = T2.group2-1 and T1.group3 = T2.group3
where T1.group1 = 0
Test DDL
CREATE TABLE T
([INID] varchar(38), [IN_InvoiceID] int, [IN_InvoiceAmount] int)
;
INSERT INTO T
([INID], [IN_InvoiceID], [IN_InvoiceAmount])
VALUES
('DB3E17E6-35C5-41:121-93B1-F809BF6B2972', 1, 2999),
('3212F048-8213-4FCC-AB64-121485B77D4E43', 2, 3737),
('E3526373-A204-40F5-801C-7F8302A4E5E2', 3, 3175),
('76CC9C19-BF79-4E8A-8034-A33805AD3390', 4, 391),
('EC7A2FBC-B62D-4865-88DE-A8097975F125', 5, 1206),
('52AD3046-21331-4F0A-BD1D-67F232C54244', 6, 402),
('CA48F132-A9F5-4516-9E58-CDEE6644AAD1', 7, 1996),
('02E10C31-CAB2-4220-B66A-CEE5E67A9378', 8, 3906),
('98F1EEFF-B07A-4B65-87F4-E165264284DD', 9, 2575),
('91EBDD8B-B73C-470C-8900-DD66078483DB', 10, 2965),
('6E2490E5-C4DE-4833-877F-1590F7BDC1B8', 11, 1603),
('00985921-AC3C-4E3E-BAE1-7F58302F831A', 12, 1302)
;
Result:
Could you please check article Display Data in Multiple Columns using SQL showing with example case how a database developer can show the list of data rows in a columnar mode using Row_Number() function and mode arithmetic expression
You need to add additional columns from the same row that is different in the sample
Seems as if you want to split the table into 2 tables with alternating 5 rows. An easy way to do this would be:
Take data into a temp table having an extra column (lets say
grouping_id)
Update the grouping id so that each 5 rows have the same id. You can
use in_invoiceId % 5 (the nod function). After this step the first 5
rows will have grouping_id 0, next 5 will have 1, next will have 2
(assuming your invoice id is incremented +1 for all rows).
You can just do a normal select with where clause for odd and even grouping_id
Ideally, you can manage with the 2 tables Master and detail table.
But due to my curiosity, I am able to solve and give the answer as
Declare #table table(id int identity, invoice_id int)
; WITH Numbers AS
(
SELECT n = 1
UNION ALL
SELECT n + 1
FROM Numbers
WHERE n+1 <= 50
)
insert into #table SELECT n
FROM Numbers
Select (a.id )%5 ,* from #table a join #table b on a.id+5 = b.id and a.id != b.id
;WITH Numbers AS
(
SELECT n = 1, o = 5
UNION ALL
SELECT n + 10, o = o+10
FROM Numbers
WHERE n+1 <= 50
)
select a.id ParentId,a.invoice_id ParentInvoiceId, --b.n, b.o,
c.invoice_id childInvoiceID from #table a
join Numbers b on a.id between b.n and b.o
left join #table c on a.id + 5 = c.id
Here is my solution
First i create grps based on whether the in_invoiceid is divisible by 5 or not.(Ignore the remainders)
After that i create a category to indicate between alternative groups(ie by checking if the remainder is 0 or otherise)
Then its a matter of dense_ranking the records on the basis of the category field ordered by in_invoiceid
Lastly a join with category=1 rows with same dense_rank as those records in category=0
create table Invoicetable(IN_ID varchar(100), IN_InvoiceID int)
INSERT INTO Invoicetable (IN_ID, IN_InvoiceID)
VALUES
('2345-BCDE-6645-1DDF', 1),
('2345-BCDE-6645-3DDF', 2),
('2345-BCDE-6645-4DDF', 3),
('2345-BCDE-6645-5DDF', 4),
('2345-BCDE-6645-6DDF', 5),
('2345-BCDE-6645-7DDF', 6),
('2345-BCDE-6645-aDDF', 7),
('2345-BCDE-6645-sDDF', 8),
('2345-BCDE-6645-dDDF', 9),
('2345-BCDE-6645-dDDF', 10),
('2345-BCDE-6645-dDDF', 11),
('2345-BCDE-6645-dDDF', 12);
with data
as (
select *
,(in_invoiceid-1)/5 as grp
,case when ((in_invoiceid-1)/5)%2=0 then '1' else '0' end as category
,dense_rank() over(partition by case when ((in_invoiceid-1)/5)%2=0 then '1' else '0' end
order by in_invoiceid) as rnk
from invoicetable a
)
select *
from data a
left join data b
on a.rnk=b.rnk
and b.category=0
where a.category=1
Here is db fiddle link.
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=287f101737c580ca271940764b2536ae
You may try with the following approach. Dividing the table is done with (((ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) - 1) / 5) % 2 = 0) which groups records in left and right groups.
CREATE TABLE #InvoiceTable(
IN_ID varchar(24),
IN_InvoiceID int
)
INSERT INTO #InvoiceTable (IN_ID, IN_InvoiceID)
VALUES
('2345-BCDE-6645-1DDF', 1),
('2345-BCDE-6645-3DDF', 2),
('2345-BCDE-6645-4DDF', 3),
('2345-BCDE-6645-5DDF', 4),
('2345-BCDE-6645-6DDF', 5),
('2345-BCDE-6645-7DDF', 6),
('2345-BCDE-6645-aDDF', 7),
('2345-BCDE-6645-sDDF', 8),
('2345-BCDE-6645-dDDF', 9),
('2345-BCDE-6645-dDDF', 10),
('2345-BCDE-6645-dDDF', 11),
('2345-BCDE-6645-dDDF', 12);
WITH cte AS (
SELECT
IN_ID,
IN_InvoiceID,
CASE
WHEN (((ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) - 1) / 5) % 2 = 0) THEN 'L'
ELSE 'R'
END AS IN_Position
FROM #InvoiceTable
),
cteL AS (
SELECT IN_ID, IN_InvoiceID, ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) AS IN_RowNumber
FROM cte
WHERE IN_Position = 'L'
),
cteR AS (
SELECT IN_ID, IN_InvoiceID, ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) AS IN_RowNumber
FROM cte
WHERE IN_Position = 'R'
)
SELECT cteL.IN_ID, cteL.IN_InvoiceID, cteR.IN_ID, cteR.IN_InvoiceID
FROM cteL
LEFT JOIN cteR ON (cteL.IN_RowNumber = cteR.IN_RowNumber)
Output:
IN_ID IN_InvoiceID IN_ID IN_InvoiceID
2345-BCDE-6645-1DDF 1 2345-BCDE-6645-7DDF 6
2345-BCDE-6645-3DDF 2 2345-BCDE-6645-aDDF 7
2345-BCDE-6645-4DDF 3 2345-BCDE-6645-sDDF 8
2345-BCDE-6645-5DDF 4 2345-BCDE-6645-dDDF 9
2345-BCDE-6645-6DDF 5 2345-BCDE-6645-dDDF 10
2345-BCDE-6645-dDDF 11 NULL NULL
2345-BCDE-6645-dDDF 12 NULL NULL

SQLite: Select record based on select result values on same table

I have a table Fields. Example:-
id remote unique_id
1 23 30007
1 24 30008
1 1 30009
2 4 30007
2 5 30008
2 1 30009
3 6 30007
3 7 30008
3 2 30009
here i want to get sum of unique_id field=30008 remote value where unique_id field=30009 remote value = 1;
basically, what i want is:
(query 1)
select SUM(remote) from Fields where unquie_id = '30008';
and one added filter where:(query 2)
select remote from Fields where unquie_id = '30009';
i want to select the _id record of query 2 where remote=1 for my query 1.
So the above output would be: 24+5=29.
Here i am selecting _id as 1,2. _id =3 would be rejected as it remote for unique_id = 30009 is 2.
This might be simple for you guys but i am new with sqlite. Hence please help.
If I understand your question correctly, then you want to sum the remote column where the unique_id is 30008, but only for those id groups having remote=1 and unique_id=30009.
SELECT SUM(f1.remote)
FROM Fields f1
INNER JOIN
(
SELECT id
FROM Fields
GROUP BY id
HAVING SUM(CASE WHEN unique_id = '30009' AND remote = 1 THEN 1 ELSE 0 END) > 0
) f2
ON f1.id = f2.id
WHERE unique_id = '30008';
I didn't actually run it, but something like this should do what you need:
select SUM(remote) from Fields as F where F.unique_id = '30008' and F.id in (select remote from Fields as G where G.unique_id = '30009');
So I tested it:
create table Fields(id integer, remote integer, unique_id integer);
insert into Fields(id, remote, unique_id) values (1, 23, 3007), (1, 24, 30008), (1 ,1 ,30009), (2, 4, 30007), (2 , 5 , 30008), (2 , 1 , 30009), (3 , 6, 30007), (3 , 7, 30008
), (3 , 2 , 30009);
/*select * from Fields;*/
select SUM(remote) from Fields as F where F.unique_id = '30008' and F.id in (select remote from Fields as G where G.unique_id = '30009');
And the ouput was: 29 as desired.
You could use exists
select sum(remote) total
from Fields f
where exists (
select 1 from Fields
where remote = f.id and unique_id = 30009
) and f.unique_id = 30008

Get all missing numbers in the sequence

The numbers are originally alpha numeric so I have a query to parse out the numbers:
My query here gives me a list of numbers:
select distinct cast(SUBSTRING(docket,7,999) as INT) from
[DHI_IL_Stage].[dbo].[Violation] where InsertDataSourceID='40' and
ViolationCounty='Carroll' and SUBSTRING(docket,5,2)='TR' and
LEFT(docket,4)='2011' order by 1
Returns the list of numbers parsed out.
For example, the number will be 2012TR557. After using the query it will be 557.
I need to write a query that will give back the missing numbers in a sequence.
Here is one approach
The following should return one row for each sequence of missing numbers. So, if you series is 3, 5, 6, 9, then it should return:
4 4
7 8
The query is:
with nums as (
select distinct cast(SUBSTRING(docket, 7, 999) as INT) as n,
row_number() over (order by cast(SUBSTRING(docket, 7, 999) as INT)) as seqnum
from [DHI_IL_Stage].[dbo].[Violation]
where InsertDataSourceID = '40' and
ViolationCounty = 'Carroll' and
SUBSTRING(docket,5,2) = 'TR' and
LEFT(docket, 4) = '2011'
)
select (nums_prev.n + 1) as first_missing, nums.n - 1 as last_missing
from nums left outer join
nums nums_prev
on nums.seqnum = nums_prev.seqnum + 1
where nums.n <> nums_prev.n + 1 ;

How can I Pivot a table in DB2? [duplicate]

This question already has answers here:
Pivoting in DB2
(3 answers)
Closed 5 years ago.
I have table A, below, where for each unique id, there are three codes with some value.
ID Code Value
---------------------
11 1 x
11 2 y
11 3 z
12 1 p
12 2 q
12 3 r
13 1 l
13 2 m
13 3 n
I have a second table B with format as below:
Id Code1_Val Code2_Val Code3_Val
Here there is just one row for each unique id. I want to populate this second table B from first table A for each id from the first table.
For the first table A above, the second table B should come out as:
Id Code1_Val Code2_Val Code3_Val
---------------------------------------------
11 x y z
12 p q r
13 l m n
How can I achieve this in a single SQL query?
select Id,
max(case when Code = '1' then Value end) as Code1_Val,
max(case when Code = '2' then Value end) as Code2_Val,
max(case when Code = '3' then Value end) as Code3_Val
from TABLEA
group by Id
SELECT Id,
max(DECODE(Code, 1, Value)) AS Code1_Val,
max(DECODE(Code, 2, Value)) AS Code2_Val,
max(DECODE(Code, 3, Value)) AS Code3_Val
FROM A
group by Id
If your version doesn't have DECODE(), you can also use this:
INSERT INTO B (id, code1_val, code2_val, code3_val)
WITH Ids (id) as (SELECT DISTINCT id
FROM A) -- Only to construct list of ids
SELECT Ids.id, a1.value, a2.value, a3.value
FROM Ids -- or substitute the actual id table
JOIN A a1
ON a1.id = ids.id
AND a1.code = 1
JOIN A a2
ON a2.id = ids.id
AND a2.code = 2
JOIN A a3
ON a3.id = ids.id
AND a3.code = 3
(Works on my V6R1 DB2 instance, and have an SQL Fiddle Example).
Here is a SQLFiddle example
insert into B (ID,Code1_Val,Code2_Val,Code3_Val)
select Id, max(V1),max(V2),max(V3) from
(
select ID,Value V1,'' V2,'' V3 from A where Code=1
union all
select ID,'' V1, Value V2,'' V3 from A where Code=2
union all
select ID,'' V1, '' V2,Value V3 from A where Code=3
) AG
group by ID
Here is the SQL Query:
insert into pivot_insert_table(id,code1_val,code2_val, code3_val)
select * from (select id,code,value from pivot_table)
pivot(max(value) for code in (1,2,3)) order by id ;
WITH Ids (id) as
(
SELECT DISTINCT id FROM A
)
SELECT Ids.id,
(select sub.value from A sub where Ids.id=sub.id and sub.code=1 fetch first rows only) Code1_Val,
(select sub.value from A sub where Ids.id=sub.id and sub.code=2 fetch first rows only) Code2_Val,
(select sub.value from A sub where Ids.id=sub.id and sub.code=3 fetch first rows only) Code3_Val
FROM Ids
You want to pivot your data. Since DB2 has no pivot function, yo can use Decode (basically a case statement.)
The syntax should be:
SELECT Id,
DECODE(Code, 1, Value) AS Code1_Val,
DECODE(Code, 2, Value) AS Code2_Val,
DECODE(Code, 3, Value) AS Code3_Val
FROM A

SQL search and destroy duplicates

I have a table with fields (simplified):
id, fld1, fld2, fld3.
id is a numeric primary key field.
There are duplicates: id differs but fld1, fld2 and fld3 are identical over 2 or more rows. There are also entries where the values occur only once, i.e. non-duplicates, of course.
Of each set of duplicate entries, I want to retain only the entry with the highest ID. I was planning to first list the doomed rows and then to delete them.
My first stab at it was this:
SELECT * FROM tab1 t1 WHERE EXISTS (
SELECT COUNT(*) FROM tab1 t2
WHERE t1.fld1 = t2.fld1 AND t1.fld2 = t2.fld2 AND t1.fld3 = t2.fld3
AND t1.id < MAX(t2.id)
HAVING COUNT(*) > 1
GROUP BY t2.fld1, t2.fld2, t2.fld3)
But (in Oracle) I'm getting a Missing right parenthesis error message. I think this needs a new approach altogether, but my SQL-fu is not up to the task. Help appreciated!
Edit:
With 'real' data fields:
select x.leg_id, x.airline_des, x.flight_nr, x.suffix, x.flight_id_date, x.lt_flight_id_date
from fdb_leg x
join ( select max(t.leg_id) 'max_id',
t.airline_des, t.flight_nr, t.suffix, t.flight_id_date, t.lt_flight_id_date
from fdb_leg t
group by t.airline_des, t.flight_nr, t.suffix, t.flight_id_date, t.lt_flight_id_date
having count(*) > 1) y on y.max_id > x.leg_id
and y.airline_des = x.airline_des and y.flight_nr = x.flight_nr and y.suffix = x.suffix
and y.flight_id_date = x.flight_id_date and x.lt_flight_id_date = y.lt_flight_id_date
Response is:
ORA-00923: FROM keyword not found where expected
Oracle 9i+, Using WITH:
To get the list of doomed entries, use:
WITH keepers AS (
SELECT MAX(t.id) 'max_id',
t.fld1, t.fld2, t.fld3
FROM TABLE_1 t
GROUP BY t.fld1, t.fld2, t.fld3
HAVING COUNT(*) > 1)
SELECT x.id,
x.fld1, x.fld2, x.fld3
FROM TABLE_1 x
JOIN keepers y ON y.max_id > x.id
AND y.fld1 = x.fld1
AND y.fld2 = x.fld2
AND y.fld3 = x.fld3
Non-WITH Equivalent:
To get the list of doomed entries, use:
SELECT x.id,
x.fld1, x.fld2, x.fld3
FROM TABLE_1 x
JOIN (SELECT MAX(t.id) 'max_id',
t.fld1, t.fld2, t.fld3
FROM TABLE_1 t
GROUP BY t.fld1, t.fld2, t.fld3
HAVING COUNT(*) > 1) y ON y.max_id > x.id
AND y.fld1 = x.fld1
AND y.fld2 = x.fld2
AND y.fld3 = x.fld3
You can delete them in one shot, like this:
SQL> create table mytable (id, fld1, fld2, fld3)
2 as
3 select 1, 1, 1, 1 from dual union all
4 select 2, 1, 1, 1 from dual union all
5 select 3, 2, 2, 2 from dual union all
6 select 4, 2, 3, 2 from dual union all
7 select 5, 2, 3, 2 from dual union all
8 select 6, 2, 3, 2 from dual
9 /
Table created.
SQL> delete mytable
2 where id not in
3 ( select max(id)
4 from mytable
5 group by fld1
6 , fld2
7 , fld3
8 )
9 /
3 rows deleted.
SQL> select * from mytable
2 /
ID FLD1 FLD2 FLD3
---------- ---------- ---------- ----------
2 1 1 1
3 2 2 2
6 2 3 2
3 rows selected.
Regards,
Rob.
Ugh, I get it. Scratch that.
This will identify the ID's needed to delete.
Select
fld1
, fld2
, fld3
, Max(ID)
From table_name
Group By
fld1
, fld2
, fld3