如何外连接两个数据集(基于多个主键)?

时间:2015-03-20 10:18:19

标签: javascript join outer-join

假设我有以下两个数据集:

   var revenueTestData = [
{"YEAR": "2007", "MONTH": "1", "CUSTOMER": "Customer1", "REVENUE": "1938.49488391425"},
{"YEAR": "2007", "MONTH": "1", "CUSTOMER": "Customer2", "REVENUE": "75.9142774343491"},
{"YEAR": "2007", "MONTH": "2", "CUSTOMER": "Customer2", "REVENUE": "99.3456067931875"}];

    var costTestData = [
{"YEAR": "2009", "MONTH": "1", "CUSTOMER": "Customer4", "COST": "14425"},
{"YEAR": "2009", "MONTH": "1", "CUSTOMER": "Customer4", "COST": "7591"},
{"YEAR": "2009", "MONTH": "2", "CUSTOMER": "Customer5", "COST": "31875"}];

我如何(在sql术语中)FULL OUTER加入两个数据集?甚至更多,我可以基于多列/主键进行操作吗?例如,在这种情况下,通过YEAR和CUSTOMER连接并获取YEAR,CUSTOMER,REVENUE的所有值,即使年份不匹配(在这种情况下,缺少的列将为null)。

我遇到了以下精美编写的函数,我可以使用它来执行LEFT JOIN并确定要在结果集中包含哪些列,但是当年份不匹配时,它们会脱离结果集(如在内部联接中应该是预期的:

function innerjoinData(primary, foreign, primaryKey, foreignKey, select) {
    var m = primary.length, n = foreign.length, index = [], c = [];

    for (var i = 0; i < m; i++) {     // loop through m items
        var row = primary[i];
        index[row[primaryKey]] = row; // create an index for primary table
    }

    for (var j = 0; j < n; j++) {     // loop through n items
        var y = foreign[j];
        var x = index[y[foreignKey]]; // get corresponding row from primary
        c.push(select(x, y));         // select only the columns you need
    }

    return c;
}

示例调用如下所示:

var testChartData= innerjoinData(revenueTestData, costTestData, "YEAR", "YEAR", function (a, b) {
                return {
                    Year: b.YEAR,
                    Cost: a.COST,
                    Revenue: b.REVENUE
                };
            });

也许有人可以帮我把它变成一个外连接?

1 个答案:

答案 0 :(得分:1)

此代码将在单个密钥上执行完全连接。您必须从两侧扫描它,您显示的代码是左连接,而不是内连接。它不应该太难以支持两个键,无限制的键将需要额外的一些。

&#13;
&#13;
function innerjoinData(primaryTable, foreignTable, primaryKey, foreignKey, selectColumns) {
    var primaryIndex = mapFromArray(primaryTable, primaryKey),
        foreignIndex = mapFromArray(foreignTable, foreignKey),
        resultSet = [];


    // Look for misses and matches from the left
    for (var i = 0; i < primaryTable.length; i++) {
        var primaryRow = primaryTable[i];
        var match = foreignIndex[primaryRow[primaryKey]];
        resultSet.push(selectColumns(primaryRow,  match || {}));
        
    }
    
    // Look for just misses from the right
    for (var i = 0; i < foreignTable.length; i++) {
        var foreignRow = foreignTable[i];
        if (!primaryIndex.hasOwnProperty( foreignRow[foreignKey] )) {
            resultSet.push(selectColumns({}, foreignRow))
        }        
    }
    

    return resultSet;

    function mapFromArray(list, keyByProp) {
        var map = {};
        for (var i = 0, item; item = list[i]; i++) {
            map[item[keyByProp]] = item;
        }
        return map;
    };
}

var revenueTestData = [{
    "YEAR": "2006",
        "MONTH": "1",
        "CUSTOMER": "Customer1",
        "REVENUE": "1938.49488391425"
}, {
    "YEAR": "2007",
        "MONTH": "1",
        "CUSTOMER": "Customer2",
        "REVENUE": "75.9142774343491"
}, {
    "YEAR": "2008",
        "MONTH": "2",
        "CUSTOMER": "Customer2",
        "REVENUE": "99.3456067931875"
}];


var costTestData = [{
    "YEAR": "2007",
        "MONTH": "1",
        "CUSTOMER": "Customer4",
        "COST": "14425"
}, {
    "YEAR": "2008",
        "MONTH": "1",
        "CUSTOMER": "Customer4",
        "COST": "7591"
}, {
    "YEAR": "2009",
        "MONTH": "2",
        "CUSTOMER": "Customer5",
        "COST": "31875"
}];


var testChartData = innerjoinData(costTestData, revenueTestData, "YEAR", "YEAR", function (primaryRow, foreignRow) {
    return {
        Year: foreignRow.YEAR || primaryRow.YEAR,
        Cost: primaryRow.COST,
        Revenue: foreignRow.REVENUE,
        Customer:foreignRow.CUSTOMER
    };
});

document.getElementById('pre').innerHTML = JSON.stringify(testChartData, null, 4)
&#13;
<pre id="pre"></pre>
&#13;
&#13;
&#13;