webForumDet fria alternativet

Programmera beroende

8 svar · 457 visningar · startad av Brimba

BrimbaMedlem sedan dec. 19992 966 inlägg
#1

Scenario:

olle 23 år gillar fotboll och innebandy söker kvinna 30-40 år som gillar matlagning

stina 32 år gillar matlagning och söker man 20-25 år som gillar fotboll och innebandy

Jag har skrivit en SP som hämtar ut alla personer som stämmer med olles sökkriteria, problemet är ju nu att när sp:n hittar stina så skall SP:n söka upp alla som Stina söker. Om olle och stina hittar varandra så skall Stina returneras till Olle, men annars inte.

Problemet är att olles sökning kan returnera 30000 poster och det skulle innebära 30000 körningar av en funktion med alla jämförelser, tester och annat blir ju detta ohållbart i hänsyn till prestanda.

Vad gör man?

LarsGMedlem sedan dec. 200012 465 inlägg
#2

Hur ser datamodellen ut?

BrimbaMedlem sedan dec. 19992 966 inlägg
#3

Det är lite av problemet att modellen är såpass dålig, men här kommer den.

ID - DESC - CAT - TYPE
1     fotboll        sport     p     
1     hockey         sport     p
1     matlagning     mat       s
1     fisk           mat       s
1     flygning       hobby     s

5     matlagning     mat       p
5     fisk                 mat       p
5     fotboll        sport     s
5     hockey         sport     s

6     matlagning     mat       p
6     flygning       hobby     p
6     rally          sport     s
6     hockey         sport     s

Där p är de personliga egenskaperna och s är de egenskaper man vill att motparten skall ha.

Jag har funderat på att generera en bitmask för varje användare, men jag vet inte hur man skall få in grupper i det.
Bitmask exempel:

100010

skulle kunna vara exempel på intressekategorin och då betyda

1 ja han skatar
0 nej han simmar inte
0 nej han kör inte rally
0 nej han cyklar inte
1 ja han klättrar
0 nej han springer inte

Problemet är att jag har flera kategorier. Jag funderade då på om man kunde mata in bitmasken i ett varcharfält och separera varje kategori med en punkt. Men då vet jag inte hur man bygger logiken för att vandra igenom kategori för att få fram både and och or.

Kanske kan man bygga ett eget index av något slag? Prioritering är hastighet, utan i så fall skulle man kunna indexera om en användare när han gör ändringar - vilket borde gå fort.

Såhär är reglerna:
Den som söker en annan person kanske söker efter två alternativ (DESC) i samma kategori (CAT), och då räcker det med en träff.

Exempelvis kan det se ut såhär:

ID - DESC - CAT - TYPE
1     fotboll        sport     p     
1     hockey         sport     p
1     matlagning     mat       s
1     fisk           mat       s
1     flygning       hobby     s

5     matlagning     mat       p
5     fisk                mat       p
5     fotboll        sport     s
5     hockey         sport     s

6     matlagning     mat       p
6     flygning       hobby     p
6     rally          sport     s
6     hockey         sport     s

Om användare 1 söker så kommer ser vi att han söker efter en person som har antingen fisk eller matlagning i kategorin mat OCH flygning som hobby.

Detta gör att han kommer att få träff på användare nummer 6 eftersom den har matlagning och flygning. Notera att användaren inte har fisk, men den är ju i samma kategori som matlagning så alltså är det eller som gäller där.
Det kommer inte att bli träff på användare nummer 5 eftersom han saknar flygning som hobby i sin personliga lista. Vi ser dock att han uppfyller både matlagning och fisk, men eftersom det är i samma kategori hjälper inte det.

Såhär ser min nuvarande kod ut (som inte fungerar)

------------------------------------------------------

SELECT 
     ID
FROM 
     dbo.GetLikes(129467) 
WHERE 
     isnull(dbo.Likes(129467, ID),-1) = 3

------------------------------------------------------

ALTER  function getLikes(@id int)
returns TABLE
AS RETURN
    (
    SELECT     
          t2.ID
    FROM         
          rum_val t1 
          INNER JOIN rum_val t2 ON 
                    t1.ID <> t2.ID
                   AND t1.DESC = t2.DESC 
                    AND t1.CAT = t2.CAT 
                    AND t2.TYPE = 'n'
    WHERE     
          t1.ID = @id
         and
         t1.TYPE = 'd'
    GROUP BY 
          t1.ID, t2.ID
    HAVING      
         (COUNT(*) >= (SELECT COUNT(DISTINCT t3.CAT) 
                                 FROM
                             rum_val t3
                                WHERE      
                        t3.ID = t1.ID
                        AND t3.TYPE = 'd'))
    )

------------------------------------------------------

ALTER  function Likes(@in_id1 int, @in_id2 int)
returns int
AS BEGIN
    DECLARE @retval int
    DECLARE @id1 int
    DECLARE @id2 int

    DECLARE @tmptbl TABLE
    ( 
          id1 int,
         id2 int
    )

    INSERT INTO 
          @tmptbl 
     SELECT     
          isnull(t1.ID,-1) id1, isnull(t2.ID,-1) id2
    FROM         
          rum_val t1 
          FULL JOIN rum_val t2 ON 
                    t1.ID <> t2.ID
                   AND t1.DESC = t2.DESC 
                    AND t1.CAT = t2.CAT 
                    AND t2.TYPE = 'n'
                   AND t2.ID=@in_id2
    WHERE     
          t1.ID = @in_id1
         and
         t1.TYPE = 'd'
         AND t2.ID=@in_id2
    GROUP BY 
          t1.ID, t2.ID
    HAVING      
         (COUNT(*) >= (SELECT COUNT(DISTINCT t3.CAT) 
                                 FROM
                             rum_val t3
                                WHERE      
                        t3.ID = t1.ID
                        AND t3.TYPE = 'd'))
    ORDER BY t2.ID

    SELECT @id1=id1, @id2=id2 FROM @tmptbl

    if (@id1>0 and @id2>0) 
     SET @retval = 3
    if (@id1>0 and @id2=-1)
    SET @retval = 2
    if (@id1=-1 and @id2>0)
    SET @retval = 1
    if (@id1=-1 and @id2=-1)
    SET @retval = 0

    RETURN @retval
END

GO

------------------------------------------------------

Hoppas ni förstår :)

LarsGMedlem sedan dec. 200012 465 inlägg
#4

Om jag kommer ihåg rätt så hade jag någon ide om att man använder en bitmask per kategori. Det blir då två poster
per person och kategori. En för de intressen som personen har själv och en för de som den sökte skall ha.

-- persone
create table person (id int primary key, name varchar(50) unique)

-- olika kategorier 
create table category(id int primary key, name varchar(30) unique, description varchar(255))

-- för att beskriva intressen inom en kategori
-- sequence_number anger vilken bit i properties som motsvarar ett visst intresse
create table propertyCategories(categoryid int references category,sequence_number int, property varchar(30)) 

-- med en bigint kan man ange 64 olika intressen inom varje kategori

create table properties (personid int references person, has char(3) check (has in ('JA','NEJ'),
   categoryid int references category,properties bigint)

Det är nog enklast att lagra en post per kategori/person oavsett om personen har något intresse inom den kategorin

Exakt hur man sätter en specifik bit i en bigint med hjälp av SQL vet jag inte. Det kanske är enklast att
hantera utanför proceduren.

För att se alla som har exakt samma intressen som given person

select * from person
 where id in (select personid from properties p1
                join properties p2 on p1.categoryid = p2.categoryid
               where p1.name = 'OLLE' 
                 and p1.has = 'Y' and p2.has = 'N'
                 and p1.property = p2.property
                 and p1.property <> 0
                 and (select count(*) from property 
                       where personid = p1.personid and property <> 0 and has = 'Y') = 
                     (select count(*) from property 
                       where personid = p2.personid and property <> 0 and has = 'N'))

För att se alla som har minst ett intresse inom alla kategorier som given person

select * from person
 where id in (select personid from properties p1
                join properties p2 on p1.categoryid = p2.categoryid
               where p1.name = 'OLLE'
                 and p1.has = 'Y' and p2.has = 'N'
                 and p1.property <> 0 and p2.property <> 0
                 and (select count(*) from properties 
                       where personid = p1.personid and property <> 0 and has = 'Y') = 
                     (select count(*) from properties 
                       where personid = p2.personid and property <> 0 and has = 'N'))

Red: Glömde lite villkor

LaspMedlem sedan juli 200010 197 inlägg
#5

Ja Det var min första tanke att skapa en bitmask av något slag.
Data är ju statiska för den sökta efter varje egen förändring.
Skall bli intressant att följa denna storys upplösning.

BrimbaMedlem sedan dec. 19992 966 inlägg
#6

Hur skall bitmasken lagras?

Jag tänkte mig ett tal exempelvis 01101 om det finns fem egenskaper på den kategorin.
Problemet är att det inte går att lagra 01101 i en bigint eftersom det blir 1101. Hade du tänkt dig att lagra det hela på något annat sätt?

Såhär eller?

x x x x x
1 2 4 8 16

Där x är varje egenskap...

Men hur får jag då reda på att minst en egenskap matchar i varje kategori? Har du någon lämplig funktion på det?

BrimbaMedlem sedan dec. 19992 966 inlägg
#7

Jag har iaf hittat vad jag söker.

"and (p1.properties & p2.properties)>0" denna gör en "bitwise and". Vilket är perfekt.

Men jag lyckas ändå inte lösa mitt problem...

Tänk er följande data:

[b]personid    has    categoryid     properties[/b]
-------------------------------------
1           1      1              9                    (egen egenskap)
1           0      1              30                   (söker)
2           1      1              7                    (egen egenskap)
2           0      1              9                    (söker)
3           1      1              7                    (egen egenskap)
3           0      1              9                    (söker)

1           0      2              7                    (söker)
2           1      2              7                    (egen egenskap)
-------------------------------------
7  =0000 0111
9  =0000 1001
30 =0001 1110

Här ser vi att person 1 söker någon som har både kategori 1 och 2 som stämmer... den skall bara hitta person 2 eftersom person 3 inte har några egenskaper i kategori 2.

Tips

BrimbaMedlem sedan dec. 19992 966 inlägg
#8

Problemet är att jag kan ju inte riktigt kolla om antalet kategorier stämmer heller eftersom person 1 kan ha kategorierna 1 och 2 medan person 3 kan ha kategorierna 1 och 3, och då skall det inte bli någon träff...
Jag vill ju inte att alla personer skall ha alla kategorier heller eftersom det kommer att fylla hela min tabell med onödigt många poster...

LarsGMedlem sedan dec. 200012 465 inlägg
#9

Om du tar med kategoritypen i join-villkoret då?

Genererad på 370 ms · cache AV · v20260730165559-full.f96bc7eb