SQL задачка (как сравнить несколько строк в таблице)

User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

SQL задачка (как сравнить несколько строк в таблице)

Post by KVA »

Есть задачка и что-то никак не вырисовывается простое и быстрое решение.

Для каждого order (ord_id) в таблице может содержаться 0 или более записей с разным IOI (ioi_id). Надо написать SQL который определяет являются ли два данных order-а разными.

Orders считаются разными если
1. у них разное количество IOI (for example ord_id=200 and ord_id=400)
2. если количество IOI одинаковое (ord_id=200,ord_id=300 или ord_id=400,ord_id=500), но разная информация ioi_amt и price не совпадают

Code: Select all

ioi_id   ord_id |  ioi_amt   price
     1      200 |        1      90
     2      200 |        2      80
     3      200 |        3      70
     4      300 |        1      90
     5      300 |        2       0
     6      300 |        3      70
     7      400 |      125      10
     8      500 |      200      50
     9      600 |      100      10
    10      700 |      100      10


Понятно что со случаем 1 проблем нет. Так же нет проблем если количество IOI = 1. А вот как сравнить в случае нескольких IOI (ord_id=200,ord_id=300)? Может кто подскажет как это сделать. Вопрос еще упирается в то что этот SQL будет частью большой и медленной stored procedure которую замедлять уже дальше просто нельзя.
Niky
Уже с Приветом
Posts: 550
Joined: 31 Mar 2000 10:01
Location: Moscow --> Baltimore, MD

Post by Niky »

Ну по простому можно так:

Code: Select all

CREATE FUNCTION dbo.CompareOrders
   (
   @order_id1 int,
   @order_id2 int
   )
RETURNS int
AS
   BEGIN
      DECLARE @items1 int, @items2 int
      SELECT @items1 = count(ioi_id)
      FROM ioi_table
      WHERE ord_id = @order_id1
      
      SELECT @items2 = count(ioi_id)
      FROM ioi_table
      WHERE ord_id = @order_id2
      
      IF (@items1 <> @items2)
        RETURN 1
       
      IF EXISTS(SELECT t1.ioi_id
      FROM ioi_table t1
      LEFT OUTER JOIN ioi_table t2 ON t2.ord_id = @order_id2
      AND t1.ioi_amt = t2.ioi_amt AND t1.price = t2.price
      WHERE t1.ord_id = @order_id1 AND t2.ioi_id IS NULL)
        RETURN 1
      
       
       RETURN 0
   END

User avatar
CTAC_P
Уже с Приветом
Posts: 6789
Joined: 01 Jun 2001 09:01

Post by CTAC_P »

Что-то вроде:
select id, money, amount, count(*) as n from table group by id, money, amount having n>1
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

Niky, IMHO IF EXISTS находит если есть _одна_ совпадающая строка, а мне надо проверить чтобы все строки совпадали.

И еще - ioi_id NOT NULL
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

CTAC_P, торможу что-то а попробовать негде до завтра. Не понял что SELECT возвращает? Количество unique сочетаний ioi_amt, price?
Niky
Уже с Приветом
Posts: 550
Joined: 31 Mar 2000 10:01
Location: Moscow --> Baltimore, MD

Post by Niky »

KVA wrote:Niky, IMHO IF EXISTS находит если есть _одна_ совпадающая строка, а мне надо проверить чтобы все строки совпадали.

И еще - ioi_id NOT NULL

Посмотрите внимательно на SELECT - он возвращает записи первого ордера, не имеющие аналога во втором. NULL в данном случае вылезает из OUTER JOIN и не имеет отношения к тому, как определена таблица.
Единственное, что там нужно добавить - сравнение сумм ордеров, чтобы отследить ситуации вроде такой: O1((1, 1), (1, 1), (2, 3)) <> O2((1, 1), (2, 3), (2, 3))
vc
Уже с Приветом
Posts: 664
Joined: 05 Jun 2002 01:11

Re: SQL задачка (как сравнить несколько строк в таблице)

Post by vc »

KVA wrote:Есть задачка и что-то никак не вырисовывается простое и быстрое решение.

Для каждого order (ord_id) в таблице может содержаться 0 или более записей с разным IOI (ioi_id). Надо написать SQL который определяет являются ли два данных order-а разными.

Orders считаются разными если
1. у них разное количество IOI (for example ord_id=200 and ord_id=400)
2. если количество IOI одинаковое (ord_id=200,ord_id=300 или ord_id=400,ord_id=500), но разная информация ioi_amt и price не совпадают

Code: Select all

ioi_id   ord_id |  ioi_amt   price
     1      200 |        1      90
     2      200 |        2      80
     3      200 |        3      70
     4      300 |        1      90
     5      300 |        2       0
     6      300 |        3      70
     7      400 |      125      10
     8      500 |      200      50
     9      600 |      100      10
    10      700 |      100      10


Понятно что со случаем 1 проблем нет. Так же нет проблем если количество IOI = 1. А вот как сравнить в случае нескольких IOI (ord_id=200,ord_id=300)? Может кто подскажет как это сделать. Вопрос еще упирается в то что этот SQL будет частью большой и медленной stored procedure которую замедлять уже дальше просто нельзя.


If you want to do it in one SQL statement, then one way might be (under MS SQL 2k):

Code: Select all

Given:

drop table t1;
create table t1(
  ioi_id int, 
  ord_id int,   
  ioi_amt int,   
  price int
);
 
insert into t1 values(  1,200,1,90 );
insert into t1 values(  2,200,0,80 );
insert into t1 values(  3,200,3,70 );
insert into t1 values(  4,300,1,90 );
insert into t1 values(  5,300,2,80 );
insert into t1 values(  6,300,3,70 );
insert into t1 values(  7,400, 125,10 );
insert into t1 values(  8,500, 200,50 );
insert into t1 values(  9,600, 100,10 );
insert into t1 values( 10,700, 100,10);

select a.ord_id, b.ord_id, 'not eq'
from t1 a join t1  b on a.ioi_amt!=b.ioi_amt and a.price!=b.price and a.ord_id < b.ord_id
group by a.ord_id, b.ord_id
union all

select a_ord_id, b_ord_id, 'eq' from
  (select a.ord_id a_ord_id, b.ord_id b_ord_id, count(*) cnt
   from t1 a join t1 b on a.ioi_amt=b.ioi_amt and a.price=b.price and a.ord_id < b.ord_id
   group by a.ord_id, b.ord_id
  ) x
  join
  (select ord_id, count(*) cnt
   from t1 group by ord_id
  ) y
  on x.a_ord_id=y.ord_id
where x.cnt=y.cnt


200   300   not eq
200   400   not eq
200   500   not eq
200   600   not eq
200   700   not eq
300   400   not eq
300   500   not eq
300   600   not eq
300   700   not eq
400   500   not eq
500   600   not eq
500   700   not eq
600   700   eq



.. or another:

Code: Select all

select 
  a.ord_id,
  b.ord_id,
  case when
    (select count(*) from t1
     where ord_id=a.ord_id)
         !=
    (select count(*)
     from t1 aa join t1 bb on aa.ioi_amt=bb.ioi_amt and aa.price=bb.price
     where aa.ord_id = a.ord_id and bb.ord_id=b.ord_id)
  then 'not eq' else 'eq' end
from (select distinct ord_id from t1) a join (select distinct ord_id from t1) b on a.ord_id < b.ord_id


Rgds.

<added>

Sorry, I goofed with the first query. It should be like this:

Code: Select all

select a.ord_id, b.ord_id, 'not eq' 
from (select distinct ord_id from t1) a join (select distinct ord_id from t1) b on a.ord_id < b.ord_id
left join (
select a_ord_id, b_ord_id from
  (select a.ord_id a_ord_id, b.ord_id b_ord_id, count(*) cnt
   from t1 a join t1 b on a.ioi_amt=b.ioi_amt and a.price=b.price and a.ord_id < b.ord_id
   group by a.ord_id, b.ord_id
  ) x
  join
  (select ord_id, count(*) cnt
   from t1 group by ord_id
  ) y
  on x.a_ord_id=y.ord_id
where x.cnt=y.cnt) c on a.ord_id=c.a_ord_id and b.ord_id=c.b_ord_id where c.a_ord_id is null

union all

select a_ord_id, b_ord_id, 'eq' from
  (select a.ord_id a_ord_id, b.ord_id b_ord_id, count(*) cnt
   from t1 a join t1 b on a.ioi_amt=b.ioi_amt and a.price=b.price and a.ord_id < b.ord_id
   group by a.ord_id, b.ord_id
  ) x
  join
  (select ord_id, count(*) cnt
   from t1 group by ord_id
  ) y
  on x.a_ord_id=y.ord_id
where x.cnt=y.cnt



</added>
zVlad
Уже с Приветом
Posts: 15311
Joined: 30 Apr 2003 16:43

Re: SQL задачка (как сравнить несколько строк в таблице)

Post by zVlad »

KVA wrote:Есть задачка и что-то никак не вырисовывается простое и быстрое решение.

Для каждого order (ord_id) в таблице может содержаться 0 или более записей с разным IOI (ioi_id). Надо написать SQL который определяет являются ли два данных order-а разными.

Orders считаются разными если
1. у них разное количество IOI (for example ord_id=200 and ord_id=400)
2. если количество IOI одинаковое (ord_id=200,ord_id=300 или ord_id=400,ord_id=500), но разная информация ioi_amt и price не совпадают

Code: Select all

ioi_id   ord_id |  ioi_amt   price
     1      200 |        1      90
     2      200 |        2      80
     3      200 |        3      70
     4      300 |        1      90
     5      300 |        2       0
     6      300 |        3      70
     7      400 |      125      10
     8      500 |      200      50
     9      600 |      100      10
    10      700 |      100      10


Понятно что со случаем 1 проблем нет. Так же нет проблем если количество IOI = 1. А вот как сравнить в случае нескольких IOI (ord_id=200,ord_id=300)? Может кто подскажет как это сделать. Вопрос еще упирается в то что этот SQL будет частью большой и медленной stored procedure которую замедлять уже дальше просто нельзя.



Здесь проблема не в SQL а в структуре данных. Если это в стадии проектирования - то структуру необходимо переделывать. Если это уже готовое приложение - то я Вам сочуствую.
SQL был разработан не для любых структур, а для реляционных структур по крайней мере в третей нормальной форме.
Конкретный вопрос - какой смысл в ioi_id? Уникальный ключ? Для чего? Насамом деле здесь просматриваются две таблицы: ORDER и ORDER_ITEM а также более четкое определение чем в Вашей системе идентифицируется ордер.
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Re: SQL задачка (как сравнить несколько строк в таблице)

Post by KVA »

zVlad wrote:Здесь проблема не в SQL а в структуре данных. Если это в стадии проектирования - то структуру необходимо переделывать. Если это уже готовое приложение - то я Вам сочуствую.


Это готовое приложение. Все как обычно. Клиент сказал "сделайте мне красиво" а слово клиента - закон.

Конкретный вопрос - какой смысл в ioi_id? Уникальный ключ? Для чего? Насамом деле здесь просматриваются две таблицы: ORDER и ORDER_ITEM а также более четкое определение чем в Вашей системе идентифицируется ордер.


ord_id уникальный ключ в tbl_order
ioi_id уникальный ключ в tbl_order_ioi

Order имеет кучу параметров сохраняемых в tbl_order. Order может иметь 0 или несколько IOI которые хранятся в tbl_order_ioi. Физический смысл IOI - сколько товара (ioi_amt) готов купить клиент по соответствующей цене (price).

В данном случае требуется сравнивать только IOI а не все параметры order. Если бы надо было сравнивать эти параметры, то и вопроса бы у меня не возникло, т.к. 1 order - 1 запись в tbl_order.

А какую структуру предложили бы вы?
vc
Уже с Приветом
Posts: 664
Joined: 05 Jun 2002 01:11

Re: SQL задачка (как сравнить несколько строк в таблице)

Post by vc »

KVA wrote:Есть задачка и что-то никак не вырисовывается простое и быстрое решение.

Для каждого order (ord_id) в таблице может содержаться 0 или более записей с разным IOI (ioi_id). Надо написать SQL который определяет являются ли два данных order-а разными.

Orders считаются разными если
1. у них разное количество IOI (for example ord_id=200 and ord_id=400)
2. если количество IOI одинаковое (ord_id=200,ord_id=300 или ord_id=400,ord_id=500), но разная информация ioi_amt и price не совпадают

Code: Select all

ioi_id   ord_id |  ioi_amt   price
     1      200 |        1      90
     2      200 |        2      80
     3      200 |        3      70
     4      300 |        1      90
     5      300 |        2       0
     6      300 |        3      70
     7      400 |      125      10
     8      500 |      200      50
     9      600 |      100      10
    10      700 |      100      10


Понятно что со случаем 1 проблем нет. Так же нет проблем если количество IOI = 1. А вот как сравнить в случае нескольких IOI (ord_id=200,ord_id=300)? Может кто подскажет как это сделать. Вопрос еще упирается в то что этот SQL будет частью большой и медленной stored procedure которую замедлять уже дальше просто нельзя.


One more variation on the same theme, probably the fastest of the three and slightly more good looking:

Code: Select all

select 
  a.ord_id,
  b.ord_id,
  case when x.cnt = y.cnt then 'eq' else 'not eq' end
from   (select distinct ord_id from t1) a
       join
       (select distinct ord_id from t1) b on a.ord_id < b.ord_id
       join
       (select ord_id, count(*) cnt from t1 group by ord_id) x  on a.ord_id=x.ord_id
       left join
       (select a.ord_id a_ord_id, b.ord_id b_ord_id, count(*) cnt
        from t1 a join t1 b on a.ioi_amt=b.ioi_amt and a.price=b.price and a.ord_id < b.ord_id
        group by a.ord_id, b.ord_id
       ) y on a.ord_id=y.a_ord_id and b.ord_id=y.b_ord_id




Rgds.
zVlad
Уже с Приветом
Posts: 15311
Joined: 30 Apr 2003 16:43

Post by zVlad »

Since there are two tables as I mentioned (I didn't know about it before) your structure is OK. Only change I would review is to replace unique ioi_id with, which says nothing, with combination of two columns: ord_id plus item_id (sequencial number of item within order). It doesn't change problem you are talking about but makes structure more meningful.
Regarding problem you raise I would ask your client what if one order is an subset of another? For example:

ioi_id ord_id | ioi_amt price
1 200 | 1 90
2 200 | 2 80
3 200 | 3 70
4 300 | 1 90
5 300 | 2 80

According to what you told us at the beginning those orders are different, but I would make it sure. Other combinations could be also found. Actually I would try to clarify and get closer to the clients ORIGINAL problem they want to resolve and how they built algorithm you share with us. The reason why is it seems like algorithm doesn't cover all possible combinations and could be expanded at any time.
Last edited by zVlad on 04 Dec 2003 16:19, edited 1 time in total.
zVlad
Уже с Приветом
Posts: 15311
Joined: 30 Apr 2003 16:43

Re: SQL задачка (как сравнить несколько строк в таблице)

Post by zVlad »

vc wrote:
KVA wrote:Есть задачка и что-то никак не вырисовывается простое и быстрое решение.

.......

One more variation on the same theme, probably the fastest of the three and slightly more good looking:

Code: Select all

select 
  a.ord_id,
  b.ord_id,
  case when x.cnt = y.cnt then 'eq' else 'not eq' end
from   (select distinct ord_id from t1) a
       join
       (select distinct ord_id from t1) b on a.ord_id < b.ord_id
       join
       (select ord_id, count(*) cnt from t1 group by ord_id) x  on a.ord_id=x.ord_id
       left join
       (select a.ord_id a_ord_id, b.ord_id b_ord_id, count(*) cnt
        from t1 a join t1 b on a.ioi_amt=b.ioi_amt and a.price=b.price and a.ord_id < b.ord_id
        group by a.ord_id, b.ord_id
       ) y on a.ord_id=y.a_ord_id and b.ord_id=y.b_ord_id




Rgds.



You kidding, vc, when you say "..slightly more good looking". Nobody but you can understand what this query is to answer for. And imagine, if original request will be "slightly" changed. Do you think any given programmer will be able to reflect any change onto such query?
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Re: SQL задачка (как сравнить несколько строк в таблице)

Post by KVA »

zVlad wrote:You kidding, vc, when you say "..slightly more good looking". Nobody but you can understand what this query is to answer for. And imagine, if original request will be "slightly" changed. Do you think any given programmer will be able to reflect any change onto such query?


Whatever. Дареному коню в зубы не смотрят. По-крайней мере я получил идею, упростил для случая когда надо сравнить только 2 order-a и все вроде работает как надо.
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

zVlad wrote:Since there are two tables as I mentioned (I didn't know about it before) your structure is OK. Only change I would review is to replace unique ioi_id with, which says nothing, with combination of two columns: ord_id plus item_id (sequencial number of item within order). It doesn't change problem you are talking about but makes structure more meningful.


Непонимаю как это сделает структуру более понятной? На самом деле абсолютно без разницы в каком порядке идут IOI. Да primary key (ioi_id) хотелось бы иметь в этой таблице.

Regarding problem you raise I would ask your client what if one order is an subset of another? For example:

ioi_id ord_id | ioi_amt price
1 200 | 1 90
2 200 | 2 80
3 200 | 3 70
4 300 | 1 90
5 300 | 2 80

According to what you told us at the beginning those orders are different, but I would make it sure.


I'm 100% sure - those orders are different.

Other combinations could be also found. Actually I would try to clarify and get closer to the clients ORIGINAL problem they want to resolve and how they built algorithm you share with us. The reason why is it seems like algorithm doesn't cover all possible combinations and could be expanded at any time.


Вроде алгоритм все комбинации покрывает. Или я что-то упускаю?
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

zVlad wrote:The reason why is it seems like algorithm doesn't cover all possible combinations and could be expanded at any time.


Damn! You are right. In this case it SELECT returns 'not eq', but business logic says that oders are equal in this case.

Code: Select all

ioi_id   ord_id |  ioi_amt   price 
     1      200 |        1      90
     2      200 |        2      80
     3      200 |        3      70
     4      300 |        1      70
     5      300 |        2      80
     6      300 |        3      90
Niky
Уже с Приветом
Posts: 550
Joined: 31 Mar 2000 10:01
Location: Moscow --> Baltimore, MD

Post by Niky »

Алгоритм vc работает, если повторы (ioi_amt, price) в пределах ордера исключены. Если повторы разрешены - не работает.
Пример:

Code: Select all

ioi_id   ord_id |  ioi_amt   price 
     17      1000 |        1      2
     18      1000 |        1      2
     19      1100 |        1      2
     20      1100 |        1      2
     21      1200 |        1      2
     22      1200 |        1      2
     23      1200 |        3      4
     24      1200 |        5      6
     25      1300 |        1      2
     26      1300 |        1      2
     27      1300 |        7      8
     28      1300 |        9      10


Результат:

Code: Select all

...
1000    1100    not eq
...
1200    1300    eq
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

Niky wrote:Алгоритм vc работает, если повторы (ioi_amt, price) в пределах ордера исключены. Если повторы разрешены - не работает.


Запрещены.
vc
Уже с Приветом
Posts: 664
Joined: 05 Jun 2002 01:11

Post by vc »

KVA wrote:
zVlad wrote:The reason why is it seems like algorithm doesn't cover all possible combinations and could be expanded at any time.


Damn! You are right. In this case it SELECT returns 'not eq', but business logic says that oders are equal in this case.

Code: Select all

ioi_id   ord_id |  ioi_amt   price 
     1      200 |        1      90
     2      200 |        2      80
     3      200 |        3      70
     4      300 |        1      70
     5      300 |        2      80
     6      300 |        3      90


That's because in my SQL (1,90) is not equal to (3, 90) etc. I assumed that the equality should be checked for whole rows...

Please define exactly what account equality means in the 'business' logic. There may be a way to solve your problem differently if the number of rows per account is limited by some reasonable figure like, say, 10.

Rgds.
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

vc wrote:Please define exactly what account equality means in the 'business' logic. There may be a way to solve your problem differently if the number of rows per account is limited by some reasonable figure like, say, 10.


Сам запутался и всех запутал. :oops: Конечно полные rows должны совпадать. Так что все работает правильно.

А что за другой путь? Обычно у order мало IOI (<5), чаще всего 1,2.
vc
Уже с Приветом
Posts: 664
Joined: 05 Jun 2002 01:11

Post by vc »

KVA wrote:
vc wrote:Please define exactly what account equality means in the 'business' logic. There may be a way to solve your problem differently if the number of rows per account is limited by some reasonable figure like, say, 10.


Сам запутался и всех запутал. :oops: Конечно полные rows должны совпадать. Так что все работает правильно.

А что за другой путь? Обычно у order мало IOI (<5), чаще всего 1,2.


You can flatten the table ('pivot') so that all the values are in one row. It's trivial to accomplish in Oracle but I need to think how to do the same in MS SQL Server ;)
What's the IOI range ? Is it like [1..25] or random ?

Rgds.
vc
Уже с Приветом
Posts: 664
Joined: 05 Jun 2002 01:11

Post by vc »

KVA wrote:
vc wrote:Please define exactly what account equality means in the 'business' logic. There may be a way to solve your problem differently if the number of rows per account is limited by some reasonable figure like, say, 10.


Сам запутался и всех запутал. :oops: Конечно полные rows должны совпадать. Так что все работает правильно.

А что за другой путь? Обычно у order мало IOI (<5), чаще всего 1,2.


In Oracle (as well as in DB2), I would do this:

Code: Select all

select a.ord_id, 
       b.ord_id,
       case when a.data=b.data then 'eq' else 'not eq' end
from
  (select ord_id,
       max(case when rn = 1 then ioi_amt else null end) ||'/'||
       max(case when rn = 2 then ioi_amt else null end) ||'/'||
       max(case when rn = 3 then ioi_amt else null end) ||'/'||
       max(case when rn = 4 then ioi_amt else null end) ||'/'||
       max(case when rn = 5 then ioi_amt else null end) ||'/'||
       max(case when rn = 1 then price else null end) ||'/'||
       max(case when rn = 2 then price else null end) ||'/'||
       max(case when rn = 3 then price else null end) ||'/'||
       max(case when rn = 4 then price else null end) ||'/'||
       max(case when rn = 5 then price else null end) data
  from
    (select ord_id, ioi_amt, price,  row_number() over (partition by ord_id order by ioi_amt) rn from t1)
  group by ord_id) a
join
  (select ord_id,
       max(case when rn = 1 then ioi_amt else null end) ||'/'||
       max(case when rn = 2 then ioi_amt else null end) ||'/'||
       max(case when rn = 3 then ioi_amt else null end) ||'/'||
       max(case when rn = 4 then ioi_amt else null end) ||'/'||
       max(case when rn = 5 then ioi_amt else null end) ||'/'||
       max(case when rn = 1 then price else null end) ||'/'||
       max(case when rn = 2 then price else null end) ||'/'||
       max(case when rn = 3 then price else null end) ||'/'||
       max(case when rn = 4 then price else null end) ||'/'||
       max(case when rn = 5 then price else null end) data
  from
    (select ord_id, ioi_amt, price,  row_number() over (partition by ord_id order by ioi_amt) rn from t1)
  group by ord_id) b on a.ord_id < b.ord_id


The code is much more efficient (only one join as opposed to four in the previous versions) and handles duplicates nicely, but I am not sure how to translate "row_number() "( SQL'99 function) to the SQL Server dialect.

Rgds.
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

vc wrote:The code is much more efficient (only one join as opposed to four in the previous versions) and handles duplicates nicely, but I am not sure how to translate "row_number() "( SQL'99 function) to the SQL Server dialect.


Thanks a lot! Я дам посмотреть этот SQL нашему DBA. Может он что-нибудь посоветует на счет диалектов. Правда я не думаю что мне это решение поможет. IOI у order-a обычно мало < 5, но мы не ограничивает юзеров сколько IOI они могут ввести.
vc
Уже с Приветом
Posts: 664
Joined: 05 Jun 2002 01:11

Post by vc »

KVA wrote:
vc wrote:The code is much more efficient (only one join as opposed to four in the previous versions) and handles duplicates nicely, but I am not sure how to translate "row_number() "( SQL'99 function) to the SQL Server dialect.


Thanks a lot! Я дам посмотреть этот SQL нашему DBA. Может он что-нибудь посоветует на счет диалектов. Правда я не думаю что мне это решение поможет. IOI у order-a обычно мало < 5, но мы не ограничивает юзеров сколько IOI они могут ввести.


The translation I came up with turned out to be quite ugly:

Code: Select all

select a.ord_id, 
       b.ord_id,
       case when a.data=b.data then 'eq' else 'not eq' end
from
  (select ord_id,
       cast(max(case when rn = 1 then ioi_amt else null end)as varchar)  +'/'+
       cast(max(case when rn = 2 then ioi_amt else null end)as varchar) +'/'+
       cast(max(case when rn = 3 then ioi_amt else null end)as varchar) +'/'+
       cast(max(case when rn = 4 then ioi_amt else null end)as varchar) +'/'+
       cast(max(case when rn = 5 then ioi_amt else null end)as varchar) +'/'+
       cast(max(case when rn = 1 then price else null end)as varchar) +'/'+
       cast(max(case when rn = 2 then price else null end)as varchar) +'/'+
       cast(max(case when rn = 3 then price else null end)as varchar) +'/'+
       cast(max(case when rn = 4 then price else null end)as varchar) +'/'+
       cast(max(case when rn = 5 then price else null end)as varchar) data
  from
    (select t1.ord_id, t1.ioi_amt, t1.price, x.cnt rn from t1 join
      (select a.ord_id, cast(a.ioi_amt as varchar)+'/'+ cast(a.price as varchar) ap, count(*) cnt
       from t1 a join t1 b on a.ord_id=b.ord_id and cast(a.ioi_amt as varchar)+'/'+cast(a.price as varchar)>= cast(b.ioi_amt as varchar)+'/'+cast(b.price as varchar)
       group by a.ord_id, cast(a.ioi_amt as varchar)+'/'+cast(a.price as varchar)) x on t1.ord_id=x.ord_id and cast(t1.ioi_amt as varchar)+'/'+cast(t1.price as varchar)=x.ap) x
  group by ord_id) a
join
  (select ord_id,
       cast(max(case when rn = 1 then ioi_amt else null end)as varchar) +'/'+
       cast(max(case when rn = 2 then ioi_amt else null end)as varchar) +'/'+
       cast(max(case when rn = 3 then ioi_amt else null end)as varchar) +'/'+
       cast(max(case when rn = 4 then ioi_amt else null end)as varchar) +'/'+
       cast(max(case when rn = 5 then ioi_amt else null end)as varchar) +'/'+
       cast(max(case when rn = 1 then price else null end)as varchar) +'/'+
       cast(max(case when rn = 2 then price else null end)as varchar) +'/'+
       cast(max(case when rn = 3 then price else null end)as varchar) +'/'+
       cast(max(case when rn = 4 then price else null end)as varchar) +'/'+
       cast(max(case when rn = 5 then price else null end)as varchar) data
  from
    (select t1.ord_id, t1.ioi_amt, t1.price, x.cnt rn from t1 join
      (select a.ord_id, cast(a.ioi_amt as varchar)+'/'+ cast(a.price as varchar) ap, count(*) cnt
       from t1 a join t1 b on a.ord_id=b.ord_id and cast(a.ioi_amt as varchar)+'/'+cast(a.price as varchar)>= cast(b.ioi_amt as varchar)+'/'+cast(b.price as varchar)
       group by a.ord_id, cast(a.ioi_amt as varchar)+'/'+cast(a.price as varchar)) x on t1.ord_id=x.ord_id and cast(t1.ioi_amt as varchar)+'/'+cast(t1.price as varchar)=x.ap) x
  group by ord_id) b on a.ord_id < b.ord_id



The SQL is not very performant because my row_number() 'implementation' added 4 extra joins although it appears to handle duplicates correctly.

For those who might bother to run the SQL above, SET CONCAT_NULL_YIELDS_NULL should be OFF to allow null concatenation (null yields an empty string). The maximum number of items per order is 5.

Rgds.
User avatar
camel
Новичок
Posts: 86
Joined: 06 Dec 2002 18:21

Post by camel »

в оракле еще вот так можно найти все одинаковые заказы - не знаю, можно ли в sql сервере написать что-нибудь подобное:

Code: Select all

SELECT a_ord_id, b_ord_id FROM
( SELECT a.ord_id a_ord_id, b.ord_id b_ord_id,
count(*)*2 - (first_value(count(*)) over (partition by a.ord_id order by abs(a.ord_id-b.ord_id)))
- (first_value(count(*)) over (partition by b.ord_id order by abs(a.ord_id-b.ord_id))) cnt
FROM t1 a, t1 b
WHERE a.price=b.price
  AND a.ioi_amt=b.ioi_amt
  AND a.ord_id <= b.ord_id
GROUP BY a.ord_id,b.ord_id)
WHERE a_ord_id <> b_ord_id
  AND cnt=0;

  A_ORD_ID   B_ORD_ID
---------- ----------
       600        700
yukami
Posts: 8
Joined: 18 Nov 2003 15:45

Post by yukami »

maybe this will give you an idea:

Code: Select all

create procedure isDif

@id_1 int,
@id_2 int

as

declare @n1 int, @n2 int

SELECT * INTO #tmp FROM privet_1 WHERE ord_id in (@id_1,@id_2)
set @n1 = @@ROWCOUNT

select @n2 = count(*) from #tmp t1, #tmp t2
where t1.ioi_amt=t1.ioi_amt and t1.price=t2.price and t1.ord_id!=t2.ord_id

drop table #tmp

select cast((@n1-@n2) as bit) as isDif

Return to “Вопросы и новости IT”