联合两个具有不同列数的表

时间:2010-02-22 09:33:02

标签: sql mysql

我有两张桌子(表A和表B)。

这些列的列数不同 - 比如说表A有更多列。

如何将这两个表联合起来并为表B没有的列获取null?

5 个答案:

答案 0 :(得分:169)

为具有较少列的表(如

)将额外列添加为null
Select Col1, Col2, Col3, Col4, Col5 from Table1
Union
Select Col1, Col2, Col3, Null as Col4, Null as Col5 from Table2

答案 1 :(得分:6)

我来到这里并按照上面的回答。但是数据类型顺序不匹配导致错误。以下来自另一个答案的描述将会派上用场。

结果是否与表格中的列序列相同?因为oracle在列顺序中是严格的。以下示例产生错误:

create table test1_1790 (
col_a varchar2(30),
col_b number,
col_c date);

create table test2_1790 (
col_a varchar2(30),
col_c date,
col_b number);

select * from test1_1790
union all
select * from test2_1790;

ORA-01790:表达式必须与对应表达式具有相同的数据类型

正如您所看到的,错误的根本原因在于使用*作为列列表说明符所暗示的不匹配列排序。通过明确输入列列表可以轻松避免此类错误:

从test1_1790中选择col_a,col_b,col_c 联合所有 从test2_1790中选择col_a,col_b,col_c; 此错误的更常见情况是当您无意中交换(或转移)SELECT列表中的两个或更多列时:

select col_a, col_b, col_c from test1_1790
union all
select col_a, col_c, col_b from test2_1790;

或者,如果以上方法无法解决您的问题,请在如下列中创建ALIAS :(查询与您的查询不同,但此处的重点是如何添加别名)专栏。)

SELECT id_table_a, 
       desc_table_a, 
       table_b.id_user as iUserID, 
       table_c.field as iField
UNION
SELECT id_table_a, 
       desc_table_a, 
       table_c.id_user as iUserID, 
       table_c.field as iField

答案 2 :(得分:1)

对于任何多余的列(如果没有映射),则将其映射为null,如以下SQL查询一样

const Discord = require('discord.js');
const client = new Discord.Client();
const ffmpegInstaller = require('@ffmpeg-installer/ffmpeg');
const ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfmpegPath(ffmpegInstaller.path);
const fs = require('fs-extra')
const mergeStream = require('merge-stream');
const config = require('./config.json');
const { getAudioDurationInSeconds } = require('get-audio-duration');
const cp = require('child_process');
const path1 = require('path');
const Enmap = require('enmap');
const UserRecords = require("./models/userrecords.js")
const ServerRecords = require("./models/serverrecords.js")
let prefix = `$`
class Readable extends require('stream').Readable { _read() {} }
let recording = false;
let currently_recording = {};
let mp3Paths = [];
const silence_buffer = new Uint8Array(3840);
const express = require('express')
const app = express()
const port = 3000
const publicIP = require('public-ip')
const { program } = require('commander');
const { path } = require('@ffmpeg-installer/ffmpeg');
const version = '0.0.1'
program.version(version);
let debug = false
let runProd = false
let fqdn = "";
const mongoose = require("mongoose");
const MongoClient = require('mongodb').MongoClient;
mongoose.connect('SECRRET',{
  useNewUrlParser: true
}, function(err){
  if(err){
    console.log(err);
  }else{
    console.log("Database connection initiated");
  }
});
require("dotenv").config()
function bufferToStream(buffer) {
    let stream = new Readable();
    stream.push(buffer);
    return stream;
}





client.commands = new Enmap();

client.on('ready', async () => {
    console.log(`Logged in as ${client.user.tag}`);

    let host = "localhost"

    

    let ip = await publicIP.v4();

    let protocol = "http";
    if (!runProd) {
        host = "localhost"
    } else {
        host = `35.226.244.186`;
    }
    fqdn = `${protocol}://${host}:${port}`
    app.listen(port, `0.0.0.0`, () => {
        console.log(`Listening on port ${port} for ${host} at fqdn ${fqdn}`)
    })
});
let randomArr = []
let finalArrWithIds = []
let variable = 0
client.on('message', async message => {
    console.log(`fuck`);
    if(message.content === `$record`){
        mp3Paths = []
        finalArrWithIds = []
        let membersToScrape = Array.from(message.member.voice.channel.members.values());
        membersToScrape.forEach((member) => {
            if(member.id === `749250882830598235`) {
                console.log(`botid`);
            }
            else {
                finalArrWithIds.push(member.id)
            }
            
        })
        const randomNumber = Math.floor(Math.random() * 100)
        randomArr = []
        randomArr.push(randomNumber)
    }
   
    
    const generateSilentData = async (silentStream, memberID) => {
        console.log(`recordingnow`)
        while(recording) {
            if (!currently_recording[memberID]) {
                silentStream.push(silence_buffer);
            }
            await new Promise(r => setTimeout(r, 20));
        }
        return "done";
    }
    console.log(generateSilentData, `status`)
    function generateOutputFile(channelID, memberID) {
        const dir = `./recordings/${channelID}/${memberID}`;
        fs.ensureDirSync(dir);
        const fileName = `${dir}/${randomArr[0]}.aac`;
        console.log(`${fileName} ---------------------------`);
        return fs.createWriteStream(fileName);
    }
    
    if (!fs.existsSync("public")) {
        fs.mkdirSync("public");
    }
    app.use("/public", express.static("./public"));
  if (!message.guild) return;

  if (message.content === config.prefix + config.record_command) {
    if (recording) {
        message.reply("bot is already recording");
        return
    }
    if (message.member.voice.channel) {
        recording = true;
        const connection = await message.member.voice.channel.join();
        const dispatcher = connection.play('./audio.mp3');

        connection.on('speaking', (user, speaking) => {
            if (speaking.has('SPEAKING')) {
                currently_recording[user.id] = true;
            } else {
                currently_recording[user.id] = false;
            }
        })


        let members = Array.from(message.member.voice.channel.members.values());
        members.forEach((member) => {

            if (member.id != client.user.id) {
                let memberStream = connection.receiver.createStream(member, {mode : 'pcm', end : 'manual'})

                let outputFile = generateOutputFile(message.member.voice.channel.id, member.id);
                console.log(outputFile, `outputfile here`);
                mp3Paths.push(outputFile.path);
                    

                silence_stream = bufferToStream(new Uint8Array(0));
                generateSilentData(silence_stream, member.id).then(data => console.log(data));
                let combinedStream = mergeStream(silence_stream, memberStream);

                ffmpeg(combinedStream)
                    .inputFormat('s32le')
                    .audioFrequency(44100)
                    .audioChannels(2)
                    .on('error', (error) => {console.log(error)})
                    .audioCodec('aac')
                    .format('adts') 
                    .pipe(outputFile)
                    
            }
        })
    } else {
      message.reply('You need to join a voice channel first!');
    }
  }

  if (message.content === config.prefix + config.stop_command) {

    let date = new Date();
    let dd = String(date.getDate()).padStart(2, '0');
    let mm = String(date.getMonth() + 1).padStart(2, '0'); 
    let yyyy = date.getFullYear();
    date = mm + '/' + dd + '/' + yyyy;





    let currentVoiceChannel = message.member.voice.channel;
    if (currentVoiceChannel) {
        recording = false;
        await currentVoiceChannel.leave();

        let mergedOutputFolder = './recordings/' + message.member.voice.channel.id + `/${randomArr[0]}/`;
        fs.ensureDirSync(mergedOutputFolder);
        let file_name = `${randomArr[0]}` + '.aac';
        let mergedOutputFile = mergedOutputFolder + file_name;
    
        
    let download_path = message.member.voice.channel.id + `/${randomArr[0]}/` + file_name;

        let mixedOutput = new ffmpeg();
        console.log(mp3Paths, `mp3pathshere`);
        mp3Paths.forEach((mp3Path) => {
             mixedOutput.addInput(mp3Path);
            
        })
        console.log(mp3Paths);
        //mixedOutput.complexFilter('amix=inputs=2:duration=longest');
        mixedOutput.complexFilter('amix=inputs=' + mp3Paths.length + ':duration=longest');
        
        let processEmbed = new Discord.MessageEmbed().setTitle(`Audio Processing.`)
        processEmbed.addField(`Audio processing starting now..`, `Processing Audio`)
        processEmbed.setThumbnail(`https://media.discordapp.net/attachments/730811581046325348/748610998985818202/speaker.png`)
        processEmbed.setColor(` #00FFFF`)
        const processEmbedMsg = await message.channel.send(processEmbed)
        async function saveMp3(mixedData, outputMixed) {
            console.log(`${mixedData} MIXED `)
            
            
            
            return new Promise((resolve, reject) => {
                mixedData.on('error', reject).on('progress',
                async (progress) => {
                    
                    let processEmbedEdit = new Discord.MessageEmbed().setTitle(`Audio Processing.`)
                    processEmbedEdit.addField(`Processing: ${progress.targetSize} KB converted`, `Processing Audio`)
                    processEmbedEdit.setThumbnail(`https://media.discordapp.net/attachments/730811581046325348/748610998985818202/speaker.png`)
                    processEmbedEdit.setColor(` #00FFFF`)
                    processEmbedMsg.edit(processEmbedEdit)
                    console.log('Processing: ' + progress.targetSize + ' KB converted');
                }).on('end', () => {
                    console.log('Processing finished !');
                    resolve()
                }).saveToFile(outputMixed);
                console.log(`${outputMixed} IT IS HERE`);
            })
        }
        // mixedOutput.saveToFile(mergedOutputFile);
        await saveMp3(mixedOutput, mergedOutputFile);
        console.log(`${mixedOutput} IN HEREEEEEEE`);
        // We saved the recording, now copy the recording
        if (!fs.existsSync(`./public`)) {
            fs.mkdirSync(`./public`);
        }
        let sourceFile = `${__dirname}/recordings/${download_path}`
        console.log(`DOWNLOAD PATH HERE ${download_path}`)
        const guildName = message.guild.id;
        const serveExist = `/public/${guildName}`
        if (!fs.existsSync(`.${serveExist}`)) {
            fs.mkdirSync(`.${serveExist}`)
        }
        let destionationFile = `${__dirname}${serveExist}/${file_name}`

        let errorThrown = false
        try {
            fs.copySync(sourceFile, destionationFile);
        } catch (err) {
            errorThrown = true
            await message.channel.send(`Error: ${err.message}`)
        }
        const usersWithTag = finalArrWithIds.map(user => `\n <@${user}>`);
        let timeSpent = await getAudioDurationInSeconds(`public/${guildName}/${file_name}`)
        let timesSpentRound = Math.floor(timeSpent)
        let finalTimeSpent = timesSpentRound / 60
        let finalTimeForReal = Math.floor(finalTimeSpent)
        if(!errorThrown){
            //--------------------- server recording save START
            class GeneralRecords {
                constructor(generalLink, date, voice, time) {
                  this.generalLink = generalLink;
                  this.date = date;
                  this.note = `no note`;
                  this.voice = voice;
                  this.time = time
                }
              }
              let newGeneralRecordClassObject = new GeneralRecords(`${fqdn}/public/${guildName}/${file_name}`, date, usersWithTag, finalTimeForReal)
              let checkingServerRecord = await ServerRecords.exists({userid: `server`})
              if(checkingServerRecord === true){
                  existingServerRecord = await ServerRecords.findOne({userid: `server`})
                  existingServerRecord.content.push(newGeneralRecordClassObject)
                  await existingServerRecord.save()
              }
              if(checkingServerRecord === false){
                let serverRecord = new ServerRecords()
                serverRecord.userid = `server`
                serverRecord.content.push(newGeneralRecordClassObject)
                await serverRecord.save()
              }
              //--------------------- server recording save STOP
        }
        
        //--------------------- personal recording section START
        for( member of finalArrWithIds) {

        let personal_download_path = message.member.voice.channel.id + `/${member}/` + file_name;
        let sourceFilePersonal = `${__dirname}/recordings/${personal_download_path}`
        let destionationFilePersonal = `${__dirname}${serveExist}/${member}/${file_name}`
        await fs.copySync(sourceFilePersonal, destionationFilePersonal);
        const user = client.users.cache.get(member);
        console.log(user, `user here`);
        try {
            ffmpeg.setFfmpegPath(ffmpegInstaller.path);
          
            ffmpeg(`public/${guildName}/${member}/${file_name}`)
             .audioFilters('silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-90dB')
             .output(`public/${guildName}/${member}/personal-${file_name}`)
             .on(`end`, function () {
               console.log(`DONE`);
             })
             .on(`error`, function (error) {
               console.log(`An error occured` + error.message)
             })
             .run();
             
          }
          catch (error) {
          console.log(error)
          }
        

        // ----------------- SAVING PERSONAL RECORDING TO DATABASE START
        class PersonalRecords {
            constructor(generalLink, personalLink, date, time) {
              this.generalLink = generalLink;
              this.personalLink = personalLink;
              this.date = date;
              this.note = `no note`;
              this.time = time;
            }
          }
          let timeSpentPersonal = await getAudioDurationInSeconds(`public/${guildName}/${file_name}`)
          let timesSpentRoundPersonal = Math.floor(timeSpentPersonal)
          let finalTimeSpentPersonal = timesSpentRoundPersonal / 60
          let finalTimeForRealPersonal = Math.floor(finalTimeSpentPersonal)
          let newPersonalRecordClassObject = new PersonalRecords(`${fqdn}/public/${guildName}/${file_name}`, `${fqdn}/public/${guildName}/${member}/personal-${file_name}`, date, finalTimeForRealPersonal)

           let checkingUserRecord = await UserRecords.exists({userid: member})
              if(checkingUserRecord === true){
                  existingUserRecord = await UserRecords.findOne({userid: member})
                  existingUserRecord.content.push(newPersonalRecordClassObject)
                  await existingUserRecord.save()
              }
              if(checkingUserRecord === false){
                let newRecord = new UserRecords()
                newRecord.userid = member
                newRecord.content.push(newPersonalRecordClassObject)
                await newRecord.save()
              }


       
        // ----------------- SAVING PERSONAL RECORDING TO DATABASE END
       

        const endPersonalEmbed = new Discord.MessageEmbed().setTitle(`Your performance was amazing ! Review it here :D`)
        endPersonalEmbed.setColor('#9400D3')
        endPersonalEmbed.setThumbnail(`https://media.discordapp.net/attachments/730811581046325348/745381641324724294/vinyl.png`)
        endPersonalEmbed.addField(`? Date`, `${date}`)
        endPersonalEmbed.addField(`⏰ Time spent by you`, `${finalTimeForRealPersonal} minute(s)`)
        endPersonalEmbed.addField(`? Download`, `${fqdn}/public/${guildName}/${member}/personal-${file_name}`)
        endPersonalEmbed.setFooter(`Use \`$myrecordings\` to get a list of all your recordings !`)

        user.send(endPersonalEmbed)
    }
         // ----------------- personal recording section OVER

        
         const endEmbed = new Discord.MessageEmbed().setTitle(`I will be honest, that recording session was sick !`)
         endEmbed.setColor('#9400D3')
         endEmbed.addField(`?️ Voices`, usersWithTag)
         endEmbed.addField(`⏰ Time spent`, `${finalTimeForReal} minute(s)`)
         endEmbed.addField(`? Download`, `${fqdn}/public/${guildName}/${file_name}`)
         endEmbed.setThumbnail(`https://media.discordapp.net/attachments/730811581046325348/745370416352067704/microphone.png`)
         endEmbed.setFooter(`Check your DMs for a recording of your personal voice during this session.`)
        message.channel.send(endEmbed)
        
        
    } else {
      message.reply('You need to join a voice channel first!');
    }









    
  } 
  if (message.content.indexOf(prefix) !== 0) return;


  const args = message.content.slice(prefix.length).trim().split(/ +/g);
  const command = args.shift().toLowerCase();

  const cmd = client.commands.get(command);

  if (!cmd) return;

  cmd.run(client, message, args);
});

fs.readdir('./commands/', async (err, files) => {
    if (err) return console.error;
    files.forEach(file => {
        if (!file.endsWith('.js')) return;
        let props = require(`./commands/${file}`);
        let cmdName = file.split('.')[0];
        console.log(`Loaded command '${cmdName}'`);

        // Register extra Listeners
        

        client.commands.set(cmdName, props);
    });
});



async function main() {
    program.option('-debug')
    program.option('-prod')

    program.parse(process.argv)

    console.log(program.opts())
    if (program.Debug != undefined) {
      debug = !debug
    }
    if (program.Prod != undefined) {
      runProd = !runProd
    }
    if (runProd) {
        client.login(process.env.DISCORD_TOKEN_PROD).catch(e => {
            console.log("ERROR")
            console.log(e)
        })
    } else {
        client.login(process.env.DISCORD_TOKEN_TEST).catch(e => {
            console.log("ERROR")
            console.log(e)
        })
    }
}
main()

答案 3 :(得分:0)

如果只有1行,则可以使用join

Select t1.Col1, t1.Col2, t1.Col3, t2.Col4, t2.Col5 from Table1 t1 join Table2 t2;

答案 4 :(得分:0)

通常,在使用基于集合的运算符时,您需要具有相同的列数,因此Kangkan's answer是正确的。

SAS SQL具有特定的运算符来处理这种情况:

  

SAS(R) 9.3 SQL Procedure User's Guide

     

CORRESPONDING(CORR)关键字

     

仅在指定集合运算符时才使用CORRESPONDING关键字。 CORR使PROC SQL通过名称而不是按序位置来匹配表表达式中的列。除了OUTER UNION运算符外,名称不匹配的列将从结果表中排除。

SELECT * FROM tabA
OUTER UNION CORR
SELECT * FROM tabB;

针对:

+---+---+
| a | b |
+---+---+
| 1 | X |
| 2 | Y |
+---+---+

OUTER UNION CORR

+---+---+
| b | d |
+---+---+
| U | 1 |
+---+---+

<=>

+----+----+---+
| a  | b  | d |
+----+----+---+
|  1 | X  |   |
|  2 | Y  |   |
|    | U  | 1 |
+----+----+---+

U-SQL支持类似的概念:

  

OUTER UNION BY NAME ON (*)

     

外部

     

需要BY NAME子句和ON列表。与其他集合表达式相反,OUTER UNION的输出模式从两侧都包括匹配列和不匹配列。这就产生了这样一种情况,其中来自一侧的每一行都有仅出现在另一侧的“缺失列”。对于此类列,将为“缺少的单元格”提供默认值。对于可为空的类型,默认值为null;对于不可为空的类型,默认值为.Net。(例如,对于int,默认值为0)。

     

按人名

     当与OUTER一起使用时,

是必需的。该子句表明并集不是根据位置而是根据列的名称来匹配值。如果未指定BY NAME子句,则匹配将在位置进行。

     

如果ON子句包含“ *”符号(可以将其指定为列表的最后一个或唯一的成员),则允许使用ON子句中的名称之外的额外名称匹配,并且结果的列包括所有匹配项列按在左参数中出现的顺序排列。

和代码:

@result =    
    SELECT * FROM @left
    OUTER UNION BY NAME ON (*) 
    SELECT * FROM @right;

编辑:

外部联合的概念由KQL支持:

  

种类:

     

内部-结果具有所有输入表共有的列子集。

     

外部-结果包含所有输入中出现的所有列。输入行未定义的单元格设置为null。

示例:

let t1 = datatable(col1:long, col2:string)  
[1, "a",  
2, "b",
3, "c"];
let t2 = datatable(col3:long)
[1,3];
t1 | union kind=outer t2;

输出:

+------+------+------+
| col1 | col2 | col3 |
+------+------+------+
|    1 | a    |      |
|    2 | b    |      |
|    3 | c    |      |
|      |      |    1 |
|      |      |    3 |
+------+------+------+

demo