MS Access SQL Query - Find record order - sql

would like to ask how do we find out the 'numbering order' of a record.
My sql query -
SELECT *
FROM tableName
WHERE field2=444
ORDER BY field1
field1 field2
1) 2/9/17 111
2) 3/9/17 222
3) 5/9/17 333
4) 8/9/17 444
5) 9/9/17 555
It would return 4 as 444 is on the Fourth record. Please advice.
p/s - we hv a large DB so select * and compare each will be not efficient
Thank you,

You can use a subquery or DCount to count all rows lower than the previous one:
SELECT (SELECT Count(Field1) FROM tableName As B WHERE b.Field1 <= a.Field1) As numberingOrder, *
FROM tableName As A
WHERE field2=444
ORDER BY field1
And for only the distinct values, as asked in comments:
SELECT (
SELECT Count(Field1)
FROM (
SELECT DISTINCT Field1 FROM tableName
) As B
WHERE b.Field1 <= a.Field1
) As numberingOrder, *
FROM tableName As A
WHERE field2=444
ORDER BY field1

You can maybe do this:
SELECT COUNT(*) as Position_Of_Where_Clause
FROM YourTable t
WHERE t.field1 <= (SELECT TOP 1 s.field1
FROM YourTable s
WHERE s.field2 = 444
ORDER BY s.field1)

If you want the fourth record and the values are all distinct, then you can use SELECT TOP twice:
SELECT TOP 1 t.*
FROM (SELECT TOP 4 t.*
FROM tableName as t
WHERE field2 = 444
ORDER BY field1
) as t
ORDER BY field1 DESC;

Related

Delete duplicate rows based on a condition

I have a table that has ID and EventDate. It has duplicate rows as I used Union of two tables. Now I got to have the rows with the minimum Eventdate and remove the other duplicates.
the table for eg
ID | Date
--- | ---
1 | 10/27/1993
1 | 10/27/1994
2 | 10/17/1993
2 | 08/15/1993
Delete duplicate rows based on condition
You can use ROW_NUMBER:
;WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY EventDate)
FROM dbo.YourTable
)
DELETE FROM CTE
WHERE RN > 1;
Use this!
delete A
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY [COLUMN] ORDER BY EventDate ASC),*
FROM dbo.Your_Table
) AS A
where rn > 1
If we talk about Firebird it is enough
DELETE FROM table1 t1_1
WHERE EXISTS(
SELECT t1_2.id FROM table1 t1_2 WHERE t1_1.EventDate>t1_2.EventDate
);
As documentation (if we about MySQL) you cannot "delete from a table and select from the same table in a subquery".
So
CREATE table1 LIKE table2;
INSERT table2 SELECT * FROM table1;
DELETE FROM table1
WHERE EXISTS(
SELECT t2.id FROM table2 t2 WHERE table1.EventDate>t2.EventDate
);
DROP TABLE table2;
Where table1 you original table.

T-SQL Statement OVER Clause Ranking Functions

I have a table which looks like this:
Field1 Field2 ValidFrom
200 a 01.01.1999
200 b 01.01.2015
210 c 01.01.2015
210 c 01.01.2010
Now I try to generate a select-statement with an additional column, that increments when Field1 stays the same, but Field2 changes. The order of the value in the extraColumn should depend on the ValidFrom, which means e.g. 1 for 01.01.1999 and 2 for 01.01.2015 (and not the other way around!).
When Field1 changes, the value in the extraColumn should start with 1 again. It should keep the same value, when the combination of Field1 and Field2 doesn't change.
So the result I'd like to have would look like this:
extraColumn Field1 Field2 ValidFrom
1 200 a 01.01.1999
2 200 b 01.01.2015
1 210 c 01.01.2015
1 210 c 01.01.2010
I tried to get this result by using this query with the RANK() function:
select RANK() OVER (
PARTITION BY [Field1], [Field2]
ORDER BY [ValidFrom] DESC
) as 'extraColumn'
,Field1 ,Field2 ,ValidFrom
FROM table1
Unfortunately this did not work as I expected it to and it did kind of the opposite of which I wanted, so my result looked like:
extraColumn Field1 Field2 ValidFrom
1 200 a 01.01.1999
1 200 b 01.01.2015
1 210 c 01.01.2015
2 210 c 01.01.2010
Any ideas what I did wrong?
Here I have answered a similar problem. Below is the adaptation to your conditions:
-- Preparation
declare #t table (
Field1 int,
Field2 char,
ValidFrom date
);
insert into #t (Field1, Field2, ValidFrom)
values
(200, 'a', '19990101'),
(200, 'b', '20150101'),
(210, 'c', '20150101'),
(210, 'c', '20100101');
-- The query
with cte as (
select t.*,
lag(t.Field2) over(partition by t.Field1 order by t.ValidFrom) as [Prev2]
from #t t
)
select c.Field1, c.Field2, c.ValidFrom,
sum(case when c.Prev2 = c.Field2 then 0 else 1 end)
over(partition by c.Field1 order by c.ValidFrom) as [ExtraColumn]
from cte c;
I only hope you aren't going to run this against millions or records, because 2 partitionings won't make it easy on the CPU and memory. Oh yes, and you need SQL Server 2012 or later for this to work.
Use DENSE_RANK, you need to use Field2 in order by to get the required result not ValidFrom
DENSE_RANK() OVER (PARTITION BY [Field1] ORDER BY [Field2])
Try it like this...
WITH
cte_MaxDate AS (
SELECT
*,
MaxDate = MAX(td.ValidFrom) OVER (PARTITION BY td.Feild1, td.Field2)
FROM
#TestData td
)
SELECT
DENSE_RANK() OVER (PARTITION BY md.Feild1 ORDER BY md.MaxDate, md.Field2),
md.Feild1, md.Field2, md.ValidFrom--, md.MaxDate
FROM
cte_MaxDate md;
I think you will have to use recursion here to calculate row-by-row whatever field2 changed or not.
WITH CTE_RN AS
(
SELECT *
, ROW_NUMBER() OVER (PARTITION BY Field1 ORDER BY ValidFrom) RN
FROM Table1
)
, RCTE AS
(
SELECT *, 1 AS ExtraColumn
FROM CTE_RN WHERE RN = 1
UNION ALL
SELECT c.*
, CASE WHEN r.Field2 = c.Field2 THEN r.ExtraColumn ELSE r.ExtraColumn + 1 END
FROM RCTE r
INNER JOIN CTE_RN c ON r.Field1 = c.Field1 AND r.RN + 1 = c.RN
)
SELECT *
FROM RCTE
ORDER BY Field1, ValidFrom
OPTION (MAXRECURSION 0)
SQLFiddle DEMO
(I added few more rows for more complex sample)

Compare two rows in two columns

I have a table for example like below
column1 column2
110 100
50 125
120 80
I want a selection in such a way that i will get something like this
column1 column2 difference
110 100 0
50 125 50
120 80 5
or just to be able to identify the difference between first row of column2 and second row of column1
You can do this with a LEFT JOIN:
SQL Fiddle
WITH Cte AS(
SELECT *,
rn = ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM tbl
)
SELECT
t1.*,
difference = ISNULL(t2.column2 - t1.column1, 0)
FROM cte t1
LEFT JOIN Cte t2
ON t1.rn = t2.rn + 1
Since there is no column to indicate the order, I added a ROW_NUMBER. Modify the ORDER BY clause to your preference.
Another way, could be this:
SELECT TB.COLUMN1,TB.COLUMN2,
(ISNULL(TB2.COLUMN2,TB.COLUMN1)-TB.COLUMN1) AS 'DIF'
FROM
(SELECT COLUMN1,COLUMN2,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS 'R' FROM TEST ) TB
LEFT JOIN
(SELECT COLUMN1,COLUMN2,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS 'R' FROM TEST ) TB2
ON TB.R = TB2.R
Until before the post I didn't know how dont let row_number 'order by' affected the query, but based on the above answer, now I Know it, using select null ;) thank you #Felix Pamittan

How to use order by with union all in sql?

I tried the sql query given below:
SELECT * FROM (SELECT *
FROM TABLE_A ORDER BY COLUMN_1)DUMMY_TABLE
UNION ALL
SELECT * FROM TABLE_B
It results in the following error:
The ORDER BY clause is invalid in views, inline functions, derived
tables, subqueries, and common table expressions, unless TOP or FOR
XML is also specified.
I need to use order by in union all. How do I accomplish this?
SELECT *
FROM
(
SELECT * FROM TABLE_A
UNION ALL
SELECT * FROM TABLE_B
) dum
-- ORDER BY .....
but if you want to have all records from Table_A on the top of the result list, the you can add user define value which you can use for ordering,
SELECT *
FROM
(
SELECT *, 1 sortby FROM TABLE_A
UNION ALL
SELECT *, 2 sortby FROM TABLE_B
) dum
ORDER BY sortby
You don't really need to have parenthesis. You can sort directly:
SELECT *, 1 AS RN FROM TABLE_A
UNION ALL
SELECT *, 2 AS RN FROM TABLE_B
ORDER BY RN, COLUMN_1
Not an OP direct response, but I thought I would jimmy in here responding to the the OP's ERROR messsage, which may point you in another direction entirely!
All these answers are referring to an overall ORDER BY once the record set has been retrieved and you sort the lot.
What if you want to ORDER BY each portion of the UNION independantly, and still have them "joined" in the same SELECT?
SELECT pass1.* FROM
(SELECT TOP 1000 tblA.ID, tblA.CustomerName
FROM TABLE_A AS tblA ORDER BY 2) AS pass1
UNION ALL
SELECT pass2.* FROM
(SELECT TOP 1000 tblB.ID, tblB.CustomerName
FROM TABLE_B AS tblB ORDER BY 2) AS pass2
Note the TOP 1000 is an arbitary number. Use a big enough number to capture all of the data you require.
There will be times when you need to do something like this :
Pull top 5 from table 1 based on a sort
and bottom 5 from table 2 based on another sort
and union these together.
solution
select * from (
-- top 5 records
select top 5 col1, col2, col3
from table1
group by col1, col2
order by col3 desc ) z
union all
select * from (
-- bottom 5 records
select top 5 col1, col2, col3
from table2
group by col1, col2
order by col3 ) z
this was the only way i was able to get around the error and worked fine for me.
SELECT * FROM (SELECT *
FROM TABLE_A ORDER BY COLUMN_1)DUMMY_TABLE
UNION ALL
SELECT * FROM TABLE_B
ORDER BY 2;
2 is column number here .. In Oracle SQL you can use the column number by which you want to sort the data
This solved my SELECT statement:
SELECT * FROM
(SELECT id,name FROM TABLE_A
UNION ALL
SELECT id,name FROM TABLE_B ) dum
order by dum.id , dum.name
where id and name columns available in tables and you can use your columns .
Simply use that , no need parenthesis or anything else
SELECT *, id as TABLE_A_ID FROM TABLE_A
UNION ALL
SELECT *, id as TABLE_B_ID FROM TABLE_B
ORDER BY TABLE_A_ID, TABLE_B_ID
ORDER BY after the last UNION should apply to both datasets joined by union.
The solution shown below:
SELECT *,id AS sameColumn1 FROM Locations
UNION ALL
SELECT *,id AS sameColumn2 FROM Cities
ORDER BY sameColumn1,sameColumn2
select CONCAT(Name, '(',substr(occupation, 1, 1), ')') AS f1
from OCCUPATIONS
union
select temp.str AS f1 from
(select count(occupation) AS counts, occupation, concat('There are a total of ' ,count(occupation) ,' ', lower(occupation),'s.') As str from OCCUPATIONS group by occupation order by counts ASC, occupation ASC
) As temp
order by f1

Fetching multiple rows based on grouping

Consider this data
PK field1 field2
1 a b
2 a (null)
3 x y
4 x z
5 q w
I need to get this data
select all columns from all rows where field1 has count >1
Which means the desired output is
PK field1 field2
1 a b
2 a (null)
3 x y
4 x z
i tried and finally settled for
select * from mytable where field1 in
(select field1 from mytable group by field1 having count(field1)>1 ) order by field1
but there has to be a better way than this
That's the way I would do it.
You could rewrite it with a join to the subquery instead of using in, but I doubt it would be any faster.
Edit: Ok, so for reference, the "join" method would go something like this:
select m.* from mytable m
join (
select field1 from mytable
group by field1
having count(field1)>1
) j on m.field1=j.field1
order by m.field1
And it seems it's worth testing to see if it's faster (thanks #binaryLV).
Another way, if using T-SQL
;WITH T AS
(
SELECT PK, FIELD1, FIELD2, COUNT(FIELD1) OVER(PARTITION BY FIELD1) AS R
)
SELECT PK, FIELD1, FIELD2
FROM T
WHERE R > 1