我有一个脚本,它上传文件并将文件名的详细信息存储在数据库中.当文档上传时,如果DOCUMENT_ID已经存在,我希望能够更新数据库中文件的名称,以增加数字,例如_1,_2,_3(在文件扩展名之前).表结构如下所示:
ID | DOCUMENT_ID | NAME | MODIFIED | USER_ID
33 | 81 | document.docx | 2014-03-21 | 1
34 | 82 | doc.docx | 2014-03-21 | 1
35 | 82 | doc.docx | 2014-03-21 | 1
36 | 82 | doc.docx | 2014-03-21 | 1
Run Code Online (Sandbox Code Playgroud)
所以在上面的例子中,我希望ID 35 NAME为doc_1.docx,ID 36 NAME为doc_2.docx.
这是我到目前为止所处的地方.我检索了上传的最后一个文件详细信息:
$result1 = mysqli_query($con,"SELECT ID, DOCUMENT_ID, NAME, MODIFIED
FROM b_bp_history ORDER BY ID DESC LIMIT 1");
while($row = mysqli_fetch_array($result1))
{
$ID = $row['ID'];
$documentID = $row['DOCUMENT_ID'];
$documentName = $row['NAME'];
$documentModified = $row['MODIFIED'];
}
Run Code Online (Sandbox Code Playgroud)
因此,这将为我提供我需要查看DOCUMENT_ID是否已存在的详细信息.现在我认为最好通过执行以下操作来查看它是否存在:
$sql = "SELECT ID, DOCUMENT_ID
FROM b_bp_history WHERE DOCUMENT_ID = $documentID";
$result2 = mysqli_query($sql);
if(mysqli_num_rows($result2) >0){
/* This is where I need my update */
} else {
/* I don't need an update in here as it will automatically add to the database
table with no number after it. Not sure if I should always add the first one
with a _1 after it so the increment is easy? */
}
Run Code Online (Sandbox Code Playgroud)
从上面我可以看到,我需要在那里进行更新,它基本上会检查名称后面是否存在数字,如果确实存在,则将其递增1.在else语句中,即如果DOCUMENT_ID尚不存在,我可以添加带有_1.docx的第一个,这样增量会更容易吗?
如果DOCUMENT_ID已经存在,则前半部分的更新需要检查扩展前的最后一个数字并增加+1,所以如果它是_1则接下来将是_2.不知道如何做到这一点.我想要的最终结果是:
ID | DOCUMENT_ID | NAME | MODIFIED | USER_ID
33 | 81 | document.docx | 2014-03-21 | 1
34 | 82 | doc.docx | 2014-03-21 | 1
35 | 82 | doc_1.docx | 2014-03-21 | 1
36 | 82 | doc_2.docx | 2014-03-21 | 1
Run Code Online (Sandbox Code Playgroud)
我希望能解释一下,谢谢你的帮助.
干杯,安迪
我曾经
MySQL 5.5.32开发并测试过这个解决方案.请务必查看我的解决方案的底部部分,了解一些家庭作业,以便将来考虑整体设计方法.
外部脚本写入文档历史记录表.有关用户提交的文件的元信息保存在此表中,包括其用户指定的名称.OP请求SQL更新语句或DML操作的过程块,它将原始文档名称重新分配给表示离散概念的名称REVISION ID.
IDDOCUMENT_ID(可能由脚本本身在外部分配的数字id)和MODIFIED(表示提交/记录文档的最新版本的时间的DATE类型值)之间的关系中.虽然其他RDBMS系统具有有用的对象和内置功能,例如Oracle的SEQUENCE对象和ANALYTICAL FUNCTIONS,但MySQL的基于SQL的功能还有一些选项.
下面是用于构建此解决方案中讨论的环境的DDL脚本.它应该与OP描述匹配一个例外(下面讨论):
CREATE TABLE document_history
(
id int auto_increment primary key,
document_id int,
name varchar(100),
modified datetime,
user_id int
);
INSERT INTO document_history (document_id, name, modified,
user_id)
VALUES
(81, 'document.docx', convert('2014-03-21 05:00:00',datetime),1),
(82, 'doc.docx', convert('2014-03-21 05:30:00',datetime),1),
(82, 'doc.docx', convert('2014-03-21 05:35:00',datetime),1),
(82, 'doc.docx', convert('2014-03-21 05:50:00',datetime),1);
COMMIT;
Run Code Online (Sandbox Code Playgroud)
该表的DOCUMENT_HISTORY设计使用了一个DATETIME名为列的类型列MODIFIED.否则,document_history表中的条目将很有可能返回围绕以下组合的业务键组合组织的查询的多个记录:DOCUMENT_ID和MODIFIED.
基于SQL的分区行计数的创造性解决方案位于较早的帖子中:@bobince的MySQL中的ROW_NUMBER().
适用于此任务的SQL查询:
select t0.document_id, t0.modified, count(*) as revision_id
from document_history as t0
join document_history as t1
on t0.document_id = t1.document_id
and t0.modified >= t1.modified
group by t0.document_id, t0.modified
order by t0.document_id asc, t0.modified asc;
Run Code Online (Sandbox Code Playgroud)
使用提供的测试数据生成此查询的结果:
| DOCUMENT_ID | MODIFIED | REVISION_ID |
|-------------|------------------------------|-------------|
| 81 | March, 21 2014 05:00:00+0000 | 1 |
| 82 | March, 21 2014 05:30:00+0000 | 1 |
| 82 | March, 21 2014 05:35:00+0000 | 2 |
| 82 | March, 21 2014 05:50:00+0000 | 3 |
Run Code Online (Sandbox Code Playgroud)
请注意,修订标识序列遵循检入每个版本的正确顺序,并且在计算与不同文档标识相关的新系列修订时,修订顺序会正确重置.
编辑: @ThomasKöhne的一个好评是考虑将其保留
REVISION_ID为版本跟踪表的持久属性.这可以从指定的文件名派生,但可能是首选,因为对单值列的索引优化更有可能起作用.仅修订版ID可用于其他目的,例如创建SORT用于查询文档历史记录的准确列.
修订标识还可以受益于其他约定:列名称宽度的大小应该适应附加的修订版ID后缀.一些MySQL字符串操作将有助于:
-- Resizing String Values:
SELECT SUBSTR('EXTRALONGFILENAMEXXX',1,17) FROM DUAL
| SUBSTR('EXTRALONGFILENAMEXXX',1,17) |
|-------------------------------------|
| EXTRALONGFILENAME |
-- Substituting and Inserting Text Within Existing String Values:
SELECT REPLACE('THE QUICK <LEAN> FOX','<LEAN>','BROWN') FROM DUAL
| REPLACE('THE QUICK <LEAN> FOX','<LEAN>','BROWN') |
|--------------------------------------------------|
| THE QUICK BROWN FOX |
-- Combining Strings Using Concatenation
SELECT CONCAT(id, '-', document_id, '-', name)
FROM document_history
| CONCAT(ID, '-', DOCUMENT_ID, '-', NAME) |
|-----------------------------------------|
| 1-81-document.docx |
| 2-82-doc.docx |
| 3-82-doc.docx |
| 4-82-doc.docx |
Run Code Online (Sandbox Code Playgroud)
使用上面的上一个查询作为基础内联视图(或子查询),这是为给定修订日志记录生成新文件名的下一步:
带有修订文件名的SQL查询
select replace(docrec.name, '.', CONCAT('_', rev.revision_id, '.')) as new_name,
rev.document_id, rev.modified
from (
select t0.document_id, t0.modified, count(*) as revision_id
from document_history as t0
join document_history as t1
on t0.document_id = t1.document_id
and t0.modified >= t1.modified
group by t0.document_id, t0.modified
order by t0.document_id asc, t0.modified asc
) as rev
join document_history as docrec
on docrec.document_id = rev.document_id
and docrec.modified = rev.modified;
Run Code Online (Sandbox Code Playgroud)
输出修改后的文件名
| NEW_NAME | DOCUMENT_ID | MODIFIED |
|-----------------|-------------|------------------------------|
| document_1.docx | 81 | March, 21 2014 05:00:00+0000 |
| doc_1.docx | 82 | March, 21 2014 05:30:00+0000 |
| doc_2.docx | 82 | March, 21 2014 05:35:00+0000 |
| doc_3.docx | 82 | March, 21 2014 05:50:00+0000 |
Run Code Online (Sandbox Code Playgroud)
这些(NEW_NAME)值是更新DOCUMENT_HISTORY表所需的值.所述的检查MODIFIED列DOCUMENT_ID= 82示出了登记入住修订以正确的顺序被编号相对于所述复合业务密钥的这一部分.
查找未处理的文档记录
如果文件名格式相当一致,则SQL LIKE运算符可能足以识别已经更改的记录名称.MySQL还提供过滤功能,通过REGULAR EXPRESSIONS解析文档名称值提供更大的灵活性.
剩下的就是弄清楚如何仅更新单个记录或一组记录.在别名表之间的连接之后,放置过滤条件的适当位置将位于查询的最外部:
...
and docrec.modified = rev.modified
WHERE docrec.id = ??? ;
Run Code Online (Sandbox Code Playgroud)
还有其他地方可以优化更快的响应时间,例如在内部子查询中导出修订版ID值...您对您感兴趣的特定记录集的了解越多,就可以对开头进行细分SQL语句只关注感兴趣的内容.
这些东西纯粹是可选的,它们代表了在设计和可用性方面想到的一些侧面想法.
两步或一步?
使用当前设计,每个记录有两个离散操作:INSERT通过脚本,然后UPDATE通过SQL DML调用获取值.必须记住两个SQL命令可能很烦人.考虑构建为仅插入操作构建的第二个表.
使用第二个表(DOCUMENT_LIST)来保存几乎相同的信息,除了可能的两列:
BASE_FILE_NAME (即doc.docx或document.docx)可以申请多个HISTORY_ID值.FILE_NAME (即doc_1.docx,doc_2.docx等),每个记录都是唯一的.TRIGGER在源表上设置数据库:DOCUMENT_HISTORY并将我们在其中开发的SQL查询放入其中.这将在脚本填充历史记录表后的大致相同时刻自动填充正确的修订文件名.
何必?此建议主要适用于
SCALABILITY您的数据库设计类别.修订名称的分配仍然是一个两步过程,但第二步现在在数据库中自动处理,而您必须记住在历史记录表顶部调用DML操作的任何地方都包含它.
管理别名
我没有在任何地方看到它,但我认为USER最初为被跟踪的文件指定了一些名称.最后,它似乎无关紧要,因为它是系统的最终用户永远不会看到的内部跟踪的东西.
为了您的信息,此信息不会描述给客户,它将作为版本历史保存在数据库的表中...
如果"基础"名称在给定后保持不变,则阅读给定文档的历史将更容易:

在上面的数据示例中,除非DOCUMENT_ID已知,否则可能不清楚列出的所有文件名是否相关.这可能不一定是个问题,但从语义的角度来看,将用户分配的文件名分开是一种很好的做法,因为ALIASES可以随时随意更改和分配.
考虑设置一个单独的表来跟踪最终用户给出的"用户友好"名称,并将其与应该表示的文档ID相关联.用户可以进行数百或数千次重命名请求...而后端文件系统使用更简单,更一致的命名方法.
| 归档时间: |
|
| 查看次数: |
1088 次 |
| 最近记录: |