9
votes

Tri javascript pour correspondre au tri SQL Server

Quelqu'un peut-il me diriger vers un algorithme de tri en JavaScript, qui trierait la même manière SQL Server (pour Nvarparar / Unicode Colonnes)?

Pour référence, ma question précédente sur ce comportement peut être trouvée ici: SQL Server 2008 - Différentes commandes de tri sur Varchar vs Nvarchar valeurs < / a>

plutôt que de tenter de modifier le comportement de tri du côté serveur, est-ce que je peux correspondre à cela du côté du client? Ma question précédente a expressément parlé des tirets dans les ordres de tri, mais je vais supposer qu'il y a un peu plus que d'ignorer simplement les tirets dans le cadre du tri.

J'ai ajouté des cas d'utilisation supplémentaires ici pour mieux démontrer le problème

échantillon de données triés à partir de SQL Server (2008): xxx

Comment puis-je obtenir JavaScript pour trier les mêmes valeurs dans le même Voies?

S'il vous plaît laissez-moi savoir si je peux plus préciser.


2 commentaires

Donc, à partir de cette question, vous voulez que le JavaScript puisse maintenant trier unicode A avant UNICODE -A ?


@Brock - Correct, bien que plus spécifiquement, je souhaite un algorithme de tri JavaScript qui correspondre au côté serveur (j'imagine qu'il y a plus à considérer que "-" caractères)


3 Réponses :


2
votes

Désolé, Javascript n'a pas de fonctionnalités de collement. La seule comparaison de chaîne que vous obtenez est directement sur les unités de code UTF-16 dans une chaîne , comme retourné par charcuteat () . .

Pour les caractères à l'intérieur du plan de base multilingue, c'est identique à une assemblée binaire, de sorte que si vous avez besoin de JS et SQL Server pour être d'accord (ignorer les avions astraux quand même), je pense que c'est la seule façon de le faire. . (À compter de la construction d'un collateur de cordes en JS et de copier méticuleusement les règles de collation de SQL Server, de toute façon. Pas beaucoup de plaisir là-bas.)

(quel est le cas d'utilisation, pourquoi doivent-ils correspondre?)


1 commentaires

Merci pour les idées; L'utilisation de l'utilisation est assez simple - j'envoie des données triées de SQL Server et possédez des fonctionnalités de tri du client dans une table. Quand ils sont en désaccord, j'ai des problèmes lors de la pagination, etc.



9
votes

Tout d'abord quelle est votre collision de base de données? Je vais supposer que c'est sql_latin1_canal_cp1_cs_as code> ou sql_latin1_general_cp1_ci_as code>. Si tel est le cas, les éléments suivants doivent fonctionner (pas complètement testé, encore).

On dirait que cela ressemble à écrire un True Strong> Soreur Unicode est une entreprise majeure. J'ai vu des codes fiscaux plus simples que les spécifications. ;-) Il semble toujours impliquer une ou plusieurs table (s) de recherche et au moins un tri de 3 niveaux - avec des caractères et des contractions de modification pour tenir compte. P>

J'ai limité le suivant au latin 1 em>, Latin Extended-A em> et Latin Extended-B EM > Tables / Collation. L'algorithme devrait fonctionner à ces ensembles assez bien, mais je ne l'ai pas complètement testé ni déprécié correctement pour modifier les caractères (pour économiser la vitesse et la complexité). P>

Voir en action sur jsbin.com . strong> p>

fonction: strong> p>

?test
^&$Grails Found
bags of Garbage
Brochures distributed
Calls Received
exhibit visitors
Exhibit Visitors
-Exhibit Visitors
--Exhibit Visitors
Ëxhibit Visitors
Grails Found


10 commentaires

J'ai vérifié que la collation de serveur est définie sur: SQL_LATIN1_GENERAL_CP1_CI_AS, je vais enquêter sur votre méthode pour voir comment il se débrouille. En côté, je pense que j'étais un peu pas cher avec la prime ... Si cela fonctionne, je lui permettra d'expirer avant d'accepter votre réponse afin que je puisse vous accorder un plus haut (ce qui semble juste / raisonnable?)


@DANP: Ne vous inquiétez pas de la prime (sauf si vous ne recevez pas une réponse satisfaisante sans un). J'aime les points, mais je fais aussi ces choses pour aider et comme un défi - au lieu de, dites, Sudoku ou un mot croisé.


Je pense que cela est à peu près aussi proche que nous pouvions espérer, merci beaucoup pour votre aide sur celui-ci (Stackoverflow a besoin d'une fonction "Acheter Beer"!)


@DANP: De rien. Si vous trouvez des cas où cela ne fonctionne pas, nous aimerions toujours savoir (mais les fixer peut être un ours).


Il y avait un ensemble de cas de test avec des traits d'union qui a échoué avec ce code, j'ai essayé de déboguer votre code, mais je n'ai pas compris très loin (il ne semble que manipuler d'ignorer des caractères au début d'une chaîne). J'ai ajouté une réponse avec les cas de test supplémentaires que j'ai utilisés.


@ ICC97, les valeurs NULL doivent être supprimées ou "Safed" avant tout tri. Travailler autour d'eux dans la fonction de tri est inefficace et inutile.


Dans mon expérience limitée, les fonctions de tri ne jettent pas une erreur si une null est transmise.


@ ICC97, ah mais ils font. Considérez cet exemple très courant: fonction CI (A, B) {retour A.Tolowcase (). Localecompare (B.Tolowercase ()); } ... NULLS dans les valeurs de tri JS ne sont pas courantes et pas une bonne pratique.


Ah vraiment très bon exemple. Pour le plugin de datatables que je suis écrit, je dois permettre aux valeurs nully de passer. Aussi avec les données SQL, il ne devrait pas être surprenant que NULLS puisse être adopté. Mais je ne suggère pas que vous ne pouvez pas Créez une fonction qui échouerait avec NULLS, mais par exemple, je serais surpris si la méthode JavaScript par défaut JavaScript a jeté une erreur. Si j'écris un plugin de tri pour les autres pour l'utiliser, il semble que ce soit à moi de gérer des données qu'elle aura probablement.


@ ICC97, la manipulation des NULLS est bonne et appropriée. Mais cela devrait être fait en une seule passe à travers la matrice, avant trier.



4
votes

@Brockadams 'Réponse est génial, mais j'avais quelques cas de bord avec des traits d'union au milieu de la ficelle que Je ne pouvais pas correspondre à SQL Server, je ne pouvais pas comprendre bien où cela allait mal, alors j'ai écrit une version plus fonctionnelle qui ne fait que filtrer les caractères ignorés, puis compare les tableaux basés sur les points de code latin.

C'est probablement moins performant, mais il y a moins de code à comprendre et qu'il fonctionne sur les correspondances des cas de test SQL que j'ai ajouté ci-dessous. p>

J'utilisais une base de données SQL Server avec Latin1_General_100_ci_as code>, il était donc insensible à la casse, mais j'ai gardé le code ici pour être sensible à la casse, il est suffisamment facile de passer à la vérification insensible à la casse, en créant une fonction d'emballage appliquant tolowercase code > Aux variables. P>

Il n'y avait pas une différence dans le tri entre les deux collations avec les cas de test que j'avais. p>

p>

/**
 * This is a modified version of sortByRoughSQL_Latin1_General_CP1_CS_AS
 * This has a more functional approach, it is more basic
 * It simply does a character filter and then sort
 * @link https://stackoverflow.com/a/3266430/327074
 *
 * @param   {String} a
 * @param   {String} b
 * @returns {Number}   -1,0,1
 */
function latinSqlSort(a, b) {
    'use strict';
    //--- This is the master lookup table for Latin1 code-points.
    //    Here through the extended set \u02AF
    var latinLookup = [
         -1,151,152,153,154,155,156,157,158,  2,  3,  4,  5,  6,159,160,161,162,163,164,
        165,166,167,168,169,170,171,172,173,174,175,176,  0,  7,  8,  9, 10, 11, 12,210,
         13, 14, 15, 41, 16,211, 17, 18, 65, 69, 71, 74, 76, 77, 80, 81, 82, 83, 19, 20,
         42, 43, 44, 21, 22,214,257,266,284,308,347,352,376,387,419,427,438,459,466,486,
        529,534,538,559,576,595,636,641,647,650,661, 23, 24, 25, 26, 27, 28,213,255,265,
        283,307,346,350,374,385,418,426,436,458,464,485,528,533,536,558,575,594,635,640,
        646,648,660, 29, 30, 31, 32,177,178,179,180,181,182,183,184,185,186,187,188,189,
        190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
          1, 33, 53, 54, 55, 56, 34, 57, 35, 58,215, 46, 59,212, 60, 36, 61, 45, 72, 75,
         37, 62, 63, 64, 38, 70,487, 47, 66, 67, 68, 39,219,217,221,231,223,233,250,276,
        312,310,316,318,392,390,395,397,295,472,491,489,493,503,495, 48,511,599,597,601,
        603,652,590,573,218,216,220,230,222,232,249,275,311,309,315,317,391,389,394,396,
        294,471,490,488,492,502,494, 49,510,598,596,600,602,651,589,655,229,228,227,226,
        235,234,268,267,272,271,270,269,274,273,286,285,290,287,324,323,322,321,314,313,
        326,325,320,319,358,357,362,361,356,355,364,363,378,377,380,379,405,404,403,402,
        401,400,407,406,393,388,417,416,421,420,432,431,428,440,439,447,446,444,443,442,
        441,450,449,468,467,474,473,470,469,477,484,483,501,500,499,498,507,506,527,526,
        540,539,544,543,542,541,561,560,563,562,567,566,565,564,580,579,578,577,593,592,
        611,610,609,608,607,606,613,612,617,616,615,614,643,642,654,653,656,663,662,665,
        664,667,666,574,258,260,262,261,264,263,281,278,277,304,292,289,288,297,335,337,
        332,348,349,369,371,382,415,409,434,433,448,451,462,476,479,509,521,520,524,523,
        531,530,552,572,571,569,570,583,582,581,585,632,631,634,638,658,657,669,668,673,
        677,676,678, 73, 79, 78,680,644, 50, 51, 52, 40,303,302,301,457,456,455,482,481,
        480,225,224,399,398,497,496,605,604,626,625,620,619,624,623,622,621,334,241,240,
        237,236,254,253,366,365,360,359,430,429,505,504,515,514,675,674,422,300,299,298,
        354,353, 84, 85, 86, 87,239,238,252,251,513,512,243,242,245,244,328,327,330,329,
        411,410,413,412,517,516,519,518,547,546,549,548,628,627,630,629, 88, 89, 90, 91,
         92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
        112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,
        132,133,134,135,136,137,138,139,140,141,142,143,246,247,248,259,279,280,293,291,
        339,336,338,331,340,341,342,423,367,373,351,370,372,383,381,384,408,414,386,445,
        453,452,454,461,463,460,475,478,465,508,522,525,532,550,553,554,555,545,556,557,
        537,551,568,333,424,343,344,586,584,618,633,637,639,645,659,649,670,671,672,679,
        681,682,683,282,686,256,345,368,375,425,435,437,535,684,685,305,296,306,591,587,
        588,144,145,146,147,148,149,150
    ];

    /**
     * A bunch of characters get ignored for the primary sort weight.
     * The most important ones are the hyphen and apostrophe characters.
     * A bunch of control characters and a couple of odds and ends, make up
     * the rest.
     *
     * @param   {Number}
     * @returns {Boolean}
     * @link https://stackoverflow.com/a/3266430/327074
     */
    function ignoreForPrimarySort(iCharCode) {
        if (iCharCode < 9) {
            return true;
        }

        if (iCharCode >= 14 && iCharCode <= 31) {
            return true;
        }

        if (iCharCode >= 127 && iCharCode <= 159) {
            return true;
        }

        if (iCharCode == 39 || iCharCode == 45 || iCharCode == 173) {
            return true;
        }

        return false;
    }

    // normal sort
    function compare(a, b) {
        if (a === b) {
            return 0;
        }
        return a > b ? 1 : -1;
    }

    // compare two arrays return first compare difference
    function arrayCompare(a, b) {
        return a.reduce(function (acc, x, i) {
            return acc === 0 && i < b.length ? compare(x, b[i]) : acc;
        }, 0);
    }

    /**
     * convert a string to array of latin code point ordering
     * @param   {String} x
     * @returns {Array}    integer array
     */
    function toLatinOrder(x) {
        return x.split('')
            // convert to char codes
            .map(function(x){return x.charCodeAt(0);})
            // filter out ignored characters
            .filter(function(x){return !ignoreForPrimarySort(x);})
            // convert to latin order
            .map(function(x){return latinLookup[x];});
    }

    // convert inputs
    var charA = toLatinOrder(a),
        charB = toLatinOrder(b);

    // compare the arrays
    var charsCompare = arrayCompare(charA, charB);
    if (charsCompare !== 0) {
        return charsCompare;
    }

    // fallback to the filtered array length
    var charsLenCompare = compare(charA.length, charB.length);
    if (charsLenCompare !== 0) {
        return charsLenCompare;
    }

    // Final fallback to a basic length comparison
    return compare(a.length, b.length);
}

var tests = [
    'Grails Found',
    '--Exhibit Visitors',
    '-Exhibit Visitors',
    'Exhibit Visitors',
    'Calls Received',
    'Ëxhibit Visitors',
    'Brochures distributed',
    'exhibit visitors',
    'bags of Garbage',
    '^&$Grails Found',
    '?test',
    '612C-520',
    '612-C-122',
    '612C-122 I',
    '612-C-126 L',
    '612C-301 B',
    '612C-304 B',
    '612C-306',
    '612-C-306',
    '612-C-306 2',
    '612-C-403 H',
    '612C403 O',
    '612-C-403(V)',
    '612E-306A/B I',
    '612E-306A/B O',
    '612C-121 O',
    '612C-111 B',
    '- -612C-111 B'
].sort(latinSqlSort).join('<br>');

document.write(tests);


2 commentaires

Pas sûr que la valeur - -612c-111 b est triée correctement, mais globalement cette réponse semble bonne (vous ne voulez pas revoir ce problème avec Rigor dès maintenant).


@Brockadams qui était en fait l'un des cas qui m'ont renvoyé ce trou de lapin. J'ai vérifié sur SQL Server - Voici un SQL Fiddle du type.