AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 316633
Accepted
Jefferson
Jefferson
Asked: 2022-09-09 06:14:18 +0800 CST2022-09-09 06:14:18 +0800 CST 2022-09-09 06:14:18 +0800 CST

Compare duas tabelas e atualize com endDate

  • 772

Eu tenho duas tabelas CustomerProfile e NewData. O CustomerProfile tem CustomerId,ProfileId,startDate e endDate. A tabela NewData é um novo dado que está chegando apenas com o ProfileId. Crio uma tabela temporária com ids de ambas as tabelas denominadas #groupedProfileIds. Em seguida, faço um loop na tabela temporária, verificando se o id está em alguma da tabela principal. Então base a contagem que eu faço um insert ou update. Eu tentei fazer isso com while e verificando a contagem de ambas as tabelas, mas queria ver se existe uma maneira melhor.

O objetivo disso é ter uma base de tabela CustomerProfile atualizada na tabela newData. Se o id não estiver na tabela newData, a tabela CustomerProfile será atualizada com uma data de término. Se forem novos Id serão inseridos com uma data de início. Eu uso ' @OldProfileId = 1 e @NewProfileId = 0' para fazer a atualização porque os dados não estão na nova tabela. Para a inserção eu faço ' @OldProfileId = 0 e @NewProfileId = 1' porque os dados estão apenas no newData. Eu não preciso marcar 1 porque eles estariam nas duas tabelas.

http://sqlfiddle.com/#!18/73845

            select * INTO #groupedProfileIds from(
            SELECT ProfileId FROM CustomerProfile
                UNION 
                SELECT ProfileId FROM NewData)  as #tmp

            declare @Rowcount int 
            select @Rowcount=count(*) from #groupedProfileIds
            while( @Rowcount>0)
            begin 
             select @Rowcount=@Rowcount-1;
             DECLARE @ProfileIds int
             set @ProfileIds = (SELECT Id FROM #groupedProfileIds order by Id desc OFFSET @Rowcount ROWS FETCH NEXT 1 ROWS ONLY);
             print @ProfileIds

             DECLARE @OldProfileId INT 
             SET @OldProfileId = (SELECT Count(ProfileId) FROM CustomerProfile where ProfileId = @ProfileIds)

             DECLARE @NewProfileId INT
             SET @NewProfileId = (SELECT Count(ProfileId) FROM #NewData where ProfileId = @ProfileIds)

             IF @OldProfileId = 1 and @NewProfileId = 0
             BEGIN
                update DBO.CustomerProfile set endDate = GETDATE() where ProfileId = @ProfileIds 

             end

             IF @OldProfileId = 0 and @NewProfileId = 1
             BEGIN
                INSERT INTO DBO. CustomerProfile (CustomerId, ProfileId,startDate)
                VALUES (CustomerId,@ProfileIds,GETDATE())
             end
sql-server sql-server-2016
  • 2 2 respostas
  • 147 Views

2 respostas

  • Voted
  1. Kazem Danesh
    2022-09-11T04:33:21+08:002022-09-11T04:33:21+08:00

    Não tenho certeza se entendi sua pergunta da maneira correta, mas se o que entendi foi verdade, a consulta abaixo compara ProfileId de duas tabelas e atualiza o campo endDate com a data atual, ele também insere profileIds da tabela newdata que não existem em CustomerProfile (o campo customerId não está disponível para eles) e preenche o campo StartDate com a data atual:

    drop table if exists #groupedProfileIds
    drop table if exists #groupedProfileIds_agg
    select * INTO #groupedProfileIds 
    from(SELECT ProfileId ,1 as flag FROM CustomerProfile
        UNION 
        SELECT ProfileId, 2 as flag FROM NewData)  as #tmp
    -- we use this flag to determine the table which record came from-------
    select * INTO #groupedProfileIds_agg
    from (select  ProfileId,sum(flag) as flag_agg
          from #groupedProfileIds
          group by ProfileId) #temp2
    -- we used second temp table to store summation of flag to avoid using aggregate function in where clause  
    -- when flag is 1  it shows the ProfileId only exists in CustomerProfile, 
    --when it is 2 the ProfileId only exists in NewData and it can be 3 if same profileId is available in both ----------
    
    update DBO.CustomerProfile set endDate = GETDATE()
            where  ProfileId in (select ProfileId from #groupedProfileIds_agg
                                    where flag_agg = 1) 
        
    insert INTO DBO. CustomerProfile (ProfileId ,startDate) 
    select ProfileId,getdate()
    from #groupedProfileIds_agg
        where flag_agg=2
        
    
    • 1
  2. Best Answer
    Gunther Schadow
    2022-09-11T09:59:37+08:002022-09-11T09:59:37+08:00

    A melhor maneira de fazer isso é criar uma consulta que tenha os dados desejados. Então você pode decidir o que inserir e o que atualizar.

    Se o id do perfil não estiver em CustomerProfile, você deseja inserir uma nova linha com um customerId, não diga de onde você obtém esse customerId, pois tudo o que você tem é um profileId?

    A deficiência pela qual você especifica seu requisito é a razão pela qual você não encontra uma solução. E ninguém pode ajudá-lo porque você não diz o que quer. De repente, inserir "câncer" em algum lugar torna tudo muito pior!

    Ninguém pode responder sua pergunta porque você não diz o que quer. No entanto, se fizermos algumas suposições, o melhor que fazemos é traçar uma direção que você pode seguir.

    Podemos adivinhar alguma coisa do seu esquema de tabela incompleto?

    CREATE TABLE CustomerProfile (
      customerProfileId integer PRIMARY KEY,
      customerId        integer NULL,
      profileId         integer,
      startDate         datetime,
      endDate           datetime NULL
    )
      
    CREATE TABLE NewData (
      profileId         integer
    )
    

    A forma como está escrito deve haver uma tabela Customer e uma tabela Profile

    CREATE TABLE Customer (
      customerId        integer PRIMARY KEY,
      ...
    )
      
    CREATE TABLE Profie (
      profileId         integer PRIMARY KEY,
      ...
    )
    

    então suas tabelas que você mencionou devem ser preenchidas como:

    CREATE TABLE CustomerProfile (
      customerProfileId integer PRIMARY KEY,
      customerId        integer REFERENCES Customer(customerId),
      profileId         integer REFERENCES Profile(profileId),
      startDate         timestamp,
      endDate           timestamp
    )
      
    CREATE TABLE NewData (
      profileId         integer REFERENCES Profile(profileId)
    )
    

    Eu primeiro retive sua não-restrição customerId NULL para me dar uma saída para não ter que me preocupar em como inferir o customerId de um mero profileId ao inserir. Mas então eu imaginei que poderia completar seu esquema, para tê-lo lógico. Isso cria tarefas adicionais para inserir uma nova linha de cliente e perfil, eu sei como posso fazer isso, mas você não pediu, então não vou fazer isso.

    Então, o acima é apenas para eu entender o que você quer. Você ainda não disse o suficiente.

    Presumo de sua tabela que seu relacionamento entre Cliente e Perfil é de muitos para muitos. E eu duvido disso. Acho que você realmente não pensou em sua lógica de dados de negócios. Mas para o que estou propondo não importa.

    Também não estou interessado em coisas do SQL Server, mas usarei o SQL padrão (meu banco de dados é o PostgreSQL). Se você não pensa em SQL (a linguagem, não o seu software), você não será um bom designer/programador/administrador de banco de dados, como quer que você se chame.

    No SQL padrão, "datetime" é chamado de "timestamp" e a maneira padrão de obter o timestamp atual é current_timestamp.

    Sobre sua data final, você diz:

    Se o id não estiver na tabela newData, a tabela CustomerProfile será atualizada com uma data de término.

    Então, ao inserir em sua tabela, presumo que startDate = current_timestamp e endDate = NULL.

    Finalmente, eu assumo que esses ids são "sequências", mas vou me abster de qualquer extensão de sequência para qualquer banco de dados e apenas determinar o próximo valor de

    SELECT max(fooId) + 1 FROM Foo

    onde "Foo" pode ser Cliente, Perfil ou Perfil do Cliente.

    Com todas essas suposições feitas, começo, como disse, do jeito que você deve sempre começar. Escreva um SELECT que lhe dará a tabela final desejada, ou, aqui, começo com a parte INSERT:

    INSERT INTO CustomerProfile(customerProfileId, customerId, profileId, startDate, endDate)
    WITH DistinctNewProfileIds AS (
      SELECT DISTINCT profileId FROM NewTable
    ), MaxCustomerProfileId AS (
      SELECT max(customerProfileId) AS maxCustomerProfileId
        FROM CustomerProfile
    ), MaxCustomerId AS (
      SELECT max(customerId) AS maxCustomerId
        FROM Customer
    ), ProfileIdWithOrdinal AS (
      SELECT rank() OVER (ORDER BY profileId) AS ordinal,
             profileId
        FROM DistinctNewProfileIds
    )
    SELECT maxCustomerProfileId + ordinal AS customerProfileId,
           maxCustomerId + ordinal AS customerId,
           profileId,
           current_timestamp AS startDate,
           NULL AS endDate
      FROM ProfileIdWithOrdinal 
      CROSS JOIN MaxCustomerProfileId
      CROSS JOIN CustomerId
    

    Agora a ATUALIZAÇÃO. Você parece esperar que todos os profileIds que ainda não terminaram apareçam em seu NewData todas as vezes. Inicialmente, entendi que você estenderia o endDate toda vez que encontrar o profileId no NewData. Isso me faz pensar o que acontece se um profileId reaparecer de repente no NewData depois que você já o perdeu uma vez e já adicionou um endDate? Você deveria apagar o endDate com NULL novamente? Você não pediu por isso, então eu não vou fazer isso. Mas você não pensou em seu design se não considerar isso.

    Aqui está a partição da sua tabela que você atualiza, após a atualização:

    WITH DistinctNewProfileIds AS (
      SELECT DISTINCT profileId FROM NewTable
    )
    SELECT customerProfileId, customerId, profileId, 
           startDate, 
           CASE WHEN p.profileId IS NULL
                THEN current_timestamp
                ELSE NULL END as endDate
      FROM CustomerProfile cp
      LEFT OUTER JOIN DistinctNewProfileIds p
        ON(p.profileId = cp.profileId)
    

    Então aqui eu realmente redefiniria seu endDate para NULL se o profileId reaparecer. Mas a atualização de qualquer maneira fica muito simples:

    UPDATE CustomerProfile 
       SET endDate = current_timestamp
     WHERE profileId NOT IN (SELECT profileId FROM NewTable)
    

    Você executa a parte UPDATE primeiro, depois o INSERT. E você está feito.

    A melhor descrição do seu requisito é uma consulta SELECT que fará o que você deseja. O que você INSERT vs. UPDATE é apenas uma questão secundária. Isso é ainda mais verdadeiro se você também tiver que inserir linhas Cliente e Perfil para atender às restrições de chave estrangeira. No PostgreSQL eu faço essas coisas em uma única consulta. Em bancos de dados menores, como Oracle ou MS SQL Server, acho que você precisa executar várias etapas em uma transação.

    • 1

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve