asp + vbscript + access,如何正确对备注型字段进行模糊检索???

woshihuzi 2005-04-12 04:23:23
我有一个数据库test.mdb,里面搜集了一些著名主持人的节目剧本,其中的表table有如下字段:

-----------------
ID:自动编号型
-----------------
编号:字符型
级别:字符型
:这些字段全是字符类型
采集人:字符型
-----------------
导语:备注型
正文:备注型
结语:备注型
-----------------

我想从正文字段当中查询包含某些字符串的文章,肯定想到用模糊查询。
我就试验了如下的语句,发现备注型字段单独构成的条件单独做where后面的条件或者跟字符类型的字段的模糊查询或者范围查询的条件合用的时候,都会出错。
请高手们帮我看一下,如何正确检索备注型字段??
莫非csdn的全文检索,就是检索数据库中的备注型字段?


具体实例如下:


正确查询的:
StrSQL = "SELECT ID,题目 FROM table where 编号 Between '1' and '3' and 级别 = '港台' and 正文 like '%丈夫%'"
StrSQL = "SELECT ID,题目 FROM table where 编号 Between '1' and '3' and 级别 = '中央'"
StrSQL = "SELECT ID,题目 FROM table where 编号 Between '1' and '3'"
StrSQL = "SELECT ID,题目 FROM table where 关键词 like '%性格%' and 题目 like '%命运%'"
StrSQL = "SELECT ID,题目 FROM table where 关键词 like '%性格%'"
StrSQL = "SELECT ID,题目 FROM table where 级别 = '港台' and 正文 like '%丈夫%'"
StrSQL = "SELECT ID,题目 FROM table where 级别 = '中央' and 采集人 = '黄伟' and 关键词 like '%性格%' and 正文 like '%丈夫%'"


不显示出错信息,但是记录总数统计为零,却能在一个已经证明为正确的分页显示系统中显示第一个页面,却不能翻页:
StrSQL = "SELECT ID,题目 FROM table where 编号 Between '1' and '2' and 正文 like '%丈夫%'"
StrSQL = "SELECT ID,题目 FROM table where 正文 like '%丈夫%'"


出错的:
StrSQL = "SELECT ID,题目 FROM table where 关键词 like '%性格%' and 题目 like '%命运%' and 正文 like '%丈夫%'"
StrSQL = "SELECT ID,题目 FROM table where 关键词 like '%性格%' and 正文 like '%丈夫%'"

出错信息如下:
===================================================
Microsoft VBScript 编译器错误 错误 '800a03f6'
缺少 'End'
/iisHelp/common/500-100.asp,行242
Microsoft OLE DB Provider for ODBC Drivers 错误 '80040e21'
ODBC 驱动程序不支持所需的属性。
/page/page2.asp,行451
===================================================

出错的行的代码:
rs_str.open StrSQL,conn,1,1
...全文
952 点赞 收藏 8
写回复
8 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
woshihuzi 2005-04-13
谢谢楼上各位。
我按照rs.open sql,conn,1,3方式打开记录集,结果又出了新问题:内存溢出
后来改用OLE DB方式,就好了。
回复
woshihuzi 2005-04-13
又不行了,请高手帮忙,详见新贴:
http://community.csdn.net/Expert/topic/3931/3931452.xml?temp=.7118647
楼上各位,还请继续提供援助。
回复
BIGbeard 2005-04-12
同意楼上的,总结一下,可有以下三种解决办法:

(一)使用rs.open sql,conn,1,3方式打开记录集

(二)将该列放在第一列取出,比如comment里存放有较长的text内容,取记录集内容的时候,先来个comment=rs("comment")把这个捣蛋的东西先取出来放到内存变量中,然后再操作其它的字段。由于一般长text内容不会在第一个显示,因此一般都要取出来放到内存变量中。

(三)改为oledb方式连接数据库。在此方式连接数据库时,不会出现该错误(起码我没有遇到过)。在我的系统中,将现在的oledb连接方式改成odbc连接方式后,错误就马上出现了。

此错误怀疑是由于Asp向odbc返回的记录集取数据的机制有些问题造成。建议采用第三种方法避免该错误。

回复
scoutlin 2005-04-12
修改ACCESS链接为OLE DB方式
回复
woshihuzi 2005-04-12
第三部分,继续构造SQL语句,实施查询及分页显示
======================================================
' 如果关于关键词的条件不为空
if check_keywords="ON" and edit_keywords<>"" then

human_condition = "关键词:包含字符串'" & edit_keywords & "'"

' 特定字符替换,在SQL语句中,%,#当作普通字符的时候,要在两边加上方括号
edit_keywords = Replace_spec_char(edit_keywords)
sql_condition = "关键词 like '%" & edit_keywords & "%'"

if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于采集人的条件不为空
if check_caijiren="ON" and edit_caijiren<>"" then

human_condition = "采集人:" & edit_caijiren

' 特定字符替换,在SQL语句中,%,#当作普通字符的时候,要在两边加上方括号
edit_caijiren = Replace_spec_char(edit_caijiren)
sql_condition = "采集人 = '" & edit_caijiren & "'"

if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于字符串检索的条件不为空
if check_string_to_find="ON" and StrToFind<>"" then

' 调试
' response.write "待查找的字符串:" & StrToFind
' response.flush

human_condition = "文章内容包含字符串'" & StrToFind & "'"

' 特定字符替换,在SQL语句中,%,#当作普通字符的时候,要在两边加上方括号
StrToFind = Replace_spec_char(StrToFind)
sql_condition = "[导语] like '%" & StrToFind & "%' or [正文] like '%" & StrToFind & "%' or [结语] like '%" & StrToFind & "%'"

if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and (" & sql_condition & ")"
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if

end if


' 条件限制完毕
' *******************************************************
if all_sql_condition<>"" then
StrSQL = StrSQL & " where " & all_sql_condition
else
all_human_condition = "所有记录"
end if

%>

<html>
<head>
<title>字符串检索结果</title>
</head>
<body bgcolor="#d0d0d0">
<P align=center><STRONG><FONT size=6 face="黑体" color="#0000FF">字符串检索结果</FONT></STRONG></p>


<%
' 调试时察看SQL查询字符串的值
response.write StrSQL
response.write "<br>"
'response.write sql_condition

' 获得记录集
rs_str.open StrSQL,conn,1,1

' 获取总页数
rec_count=rs_str.recordcount
response.write "<br>记录的总数:" & rec_count & "<br>"

' 设置每页的最大值
rs_str.pagesize=MaxPerPage

' 获取当前页号赋值给给CurrentPage,不管记录数目是不是为0,当前页总可以赋值为第一页
If trim(Request("Page"))<>"" then
CurrentPage= CLng(request("Page"))
If CurrentPage> rs_str.PageCount then
CurrentPage = rs_str.PageCount
elseif CurrentPage < 1 then
CurrentPage = 1
end if
Else
CurrentPage= 1
End If
rs_str.absolutepage = CurrentPage


' 设置最大页的序号n(从1开始,如果记录集为空,仍认为有1页)
dim n
n = rs_str.pagecount
if n = 0 then
n = 1
end if
%>

<P align=left>
<center>
<hr width="90%">
<table border="0" cellpadding="4" cellspacing="0" width="90%">
<tr>
<td width="100%" align="left">
<FONT size=2><FONT color="#00ffff"><%="查询条件: "%></FONT><FONT color="#FF6600"><%=all_human_condition%></FONT></FONT>
</td>
</tr>
</table>
<hr width="90%">
</center>

<% ' 下面开始输出表里面的记录 %>
<TABLE border=1 align=center cellpadding=2 border=1 width="690" cellspacing="0">
<tr>
<%
for i=1 to rs_str.fields.count-1
%>
<td align=CENTER bgcolor="#800000" style="border-style: ridge; border-width: 1" height="25" width="90"> <font color="#ffffff" size="2"><B><%=rs_str(i).name%></B></font></td>
<%
next
%>
</tr>
<%
' 然后根据当前页面序号和页面大小输出范围内的每一条记录
i=0
do while not rs_str.eof and i<maxperpage
%>
<tr>
<td align=center bgcolor="#F7EFDE" style="border-style: ridge; border-width: 1" width="90"><font size="2"> <%=rs_str("编号")%> </font></td>
<td bgcolor="#F7EFDE" style="border-style: ridge; border-width: 1" width="135"><font size="2"> <%=rs_str("单位")%> </font></td>
<td bgcolor="#F7EFDE" style="border-style: ridge; border-width: 1" width="115"><font size="2"> <%=rs_str("栏目")%> </font></td>
<td bgcolor="#F7EFDE" style="border-style: ridge; border-width: 1" width="350"><font size="2"> 
<a href='access-syl-onerec.asp?RecID=<%=rs_str("ID")%>' target="_blank"><%=rs_str("题目")%></a> </font></td>

</tr>
<%
i=i+1
rs_str.movenext
loop
%>
</TABLE>
<center>
<hr width="90%">
<font color="#FF00FF">第 <font color="#008080"><b><%=currentpage%></b></font> 页</font>  
<font color="#FF00FF">共 <font color="#008080"><b><%=n%></b></font> 页</font>  
<font color="#FF00FF">共 <font color="#008080"><b><%=rs_str.recordcount%></b></font> 条记录</font>     
<%
if currentPage<>1 then
new_value=old_value & "&page=1"
%>
[<b><a href='<%= Request.ServerVariables("Script_name")%>?<%=new_value%>'>首页</a></b>]
<%
new_value=old_value & "&page=" & cstr(currentPage-1)
%>
[<b><a href='<%= Request.ServerVariables("Script_name")%>?<%=new_value%>'>上一页</a></b>]
<%
else
%>
[首页] [上一页]
<%
end if

if currentPage<n then
new_value=old_value & "&page=" & cstr(currentPage+1)
%>
[<b><a href='<%= Request.ServerVariables("Script_name")%>?<%=new_value%>'>下一页</a></b>]
<%
new_value=old_value & "&page=" & cstr(n)
%>
[<b><a href='<%= Request.ServerVariables("Script_name")%>?<%=new_value%>'>尾页</a></b>]
<%
else
%>
[下一页] [尾页]
<%
end if
%>
</center>
<hr width="90%">
</P>
<%
'关闭数据库
rs_str.close
set rs_str=nothing
conn.close
set conn=nothing

%>

</body>
回复
woshihuzi 2005-04-12

第二部分,构造sql查询语句
=============================================================================

' 根据用户在提交的表单当中设定的查询条件来构造SQL语句的总的限制条件
all_sql_condition=""
all_human_condition=""
' ***********************************************************************

' 如果关于编号的条件不为空
if check_bianhao="ON" and edit_bianhao_lower<>"" and edit_bianhao_upper<>"" then
' 关于编号的条件查询,让编号字段的值介于edit_bianhao_lower和edit_bianhao_upper之间
sql_condition = "编号 Between '" & edit_bianhao_lower & "' And '" & edit_bianhao_upper & "'"
human_condition = "编号:" & edit_bianhao_lower & "-" & edit_bianhao_upper
if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于级别的条件不为空
if check_jibie="ON" and combo_jibie<>"请选择" then
sql_condition = "级别 = '" & combo_jibie & "'"
human_condition = "级别:" & combo_jibie
if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于单位的条件不为空
if check_danwei="ON" and edit_danwei<>"" then

human_condition = "单位:" & edit_danwei

' 特定字符替换,在SQL语句中,%,#当作普通字符的时候,要在两边加上方括号
edit_danwei = Replace_spec_char(edit_danwei)
sql_condition = "单位 = '" & edit_danwei & "'"

if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于形式的条件不为空
if check_xingshi="ON" and combo_xingshi<>"请选择" then
sql_condition = "形式 = '" & combo_xingshi & "'"
human_condition = "形式:" & combo_xingshi
if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于语体的条件不为空
if check_yuti="ON" and combo_yuti<>"请选择" then
sql_condition = "语体 = '" & combo_yuti & "'"
human_condition = "语体:" & combo_yuti
if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于内容的条件不为空
if check_neirong="ON" and combo_neirong<>"请选择" then
sql_condition = "内容 = '" & combo_neirong & "'"
human_condition = "内容:" & combo_neirong
if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于对象的条件不为空
if check_duixiang="ON" and combo_duixiang<>"请选择" then
sql_condition = "对象 = '" & combo_duixiang & "'"
human_condition = "对象:" & combo_duixiang
if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于栏目的条件不为空
if check_lanmu="ON" and edit_lanmu<>"" then

human_condition = "栏目:" & edit_lanmu
' 特定字符替换,在SQL语句中,%,#当作普通字符的时候,要在两边加上方括号
edit_lanmu = Replace_spec_char(edit_lanmu)
sql_condition = "栏目 = '" & edit_lanmu & "'"

if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于子栏目的条件不为空
if check_zilanmu="ON" and edit_zilanmu<>"" then

human_condition = "子栏目:" & edit_zilanmu

' 特定字符替换,在SQL语句中,%,#当作普通字符的时候,要在两边加上方括号
edit_zilanmu = Replace_spec_char(edit_zilanmu)
sql_condition = "子栏目 = '" & edit_zilanmu & "'"

if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于题目的条件不为空
if check_timu="ON" and edit_timu<>"" then

human_condition = "题目:包含字符串'" & edit_timu & "'"

' 特定字符替换,在SQL语句中,%,#当作普通字符的时候,要在两边加上方括号
edit_timu = Replace_spec_char(edit_timu)
sql_condition = "题目 like '%" & edit_timu & "%'"

if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if

' 如果关于作者的条件不为空
if check_zuozhe="ON" and edit_zuozhe<>"" then

human_condition = "作者:" & edit_zuozhe

' 特定字符替换,在SQL语句中,%,#当作普通字符的时候,要在两边加上方括号
edit_zuozhe = Replace_spec_char(edit_zuozhe)
sql_condition = "作者 = '" & edit_zuozhe & "'"

if all_sql_condition<>"" then
all_sql_condition=all_sql_condition & " and " & sql_condition
all_human_condition=all_human_condition & human_sepe_str & human_condition
else
all_sql_condition=sql_condition
all_human_condition=human_condition
end if
end if
回复
woshihuzi 2005-04-12
老大,代码太长了。高手们又没有耐心看啊????

===================================================
第一部分,主要是变量定义以及获取用户提交的查询条件

<%@ Language=VBScript%>
<%
'access-syl.asp
Option Explicit
Response.Expires = 0 '这一句可以确保所看到的数据不是缓存中的数据, 而是服务器端处理过的的最新数据


'--------------------------------------------------------------------------
' 特定字符替换,在SQL语句中,%,#当作普通字符的时候,要在两边加上方括号
'--------------------------------------------------------------------------
Function Replace_spec_char(str)
str = Replace(str, "%", "[%]")
str = Replace(str, "#", "[#]")
Replace_spec_char = str
End Function


' 定义变量接受请求表单提交过来的数据
dim check_bianhao, edit_bianhao_lower, edit_bianhao_upper ' 关于编号的几个变量
check_bianhao = Trim(Request.QueryString("check_bianhao")) 'Trim函数用来除首尾空格
edit_bianhao_lower = Trim(Request.QueryString("edit_bianhao_lower"))
edit_bianhao_upper = Trim(Request.QueryString("edit_bianhao_upper"))

dim check_jibie, combo_jibie ' 关于级别的几个变量
check_jibie = Trim(Request.QueryString("check_jibie"))
combo_jibie = Trim(Request.QueryString("combo_jibie"))

dim check_danwei, edit_danwei ' 关于单位的几个变量
check_danwei = Trim(Request.QueryString("check_danwei"))
edit_danwei = Trim(Request.QueryString("edit_danwei"))

dim check_xingshi, combo_xingshi ' 关于形式的几个变量
check_xingshi = Trim(Request.QueryString("check_xingshi"))
combo_xingshi = Trim(Request.QueryString("combo_xingshi"))

dim check_yuti, combo_yuti ' 关于语体的几个变量
check_yuti = Trim(Request.QueryString("check_yuti"))
combo_yuti = Trim(Request.QueryString("combo_yuti"))

dim check_neirong, combo_neirong ' 关于内容的几个变量
check_neirong = Trim(Request.QueryString("check_neirong"))
combo_neirong = Trim(Request.QueryString("combo_neirong"))

dim check_duixiang, combo_duixiang ' 关于对象的几个变量
check_duixiang = Trim(Request.QueryString("check_duixiang"))
combo_duixiang = Trim(Request.QueryString("combo_duixiang"))

dim check_lanmu, edit_lanmu ' 关于栏目的几个变量
check_lanmu = Trim(Request.QueryString("check_lanmu"))
edit_lanmu = Trim(Request.QueryString("edit_lanmu"))

dim check_zilanmu, edit_zilanmu ' 关于子栏目的几个变量
check_zilanmu = Trim(Request.QueryString("check_zilanmu"))
edit_zilanmu = Trim(Request.QueryString("edit_zilanmu"))

dim check_timu, edit_timu ' 关于题目的几个变量
check_timu = Trim(Request.QueryString("check_timu"))
edit_timu = Trim(Request.QueryString("edit_timu"))

dim check_zuozhe, edit_zuozhe ' 关于作者的几个变量
check_zuozhe = Trim(Request.QueryString("check_zuozhe"))
edit_zuozhe = Trim(Request.QueryString("edit_zuozhe"))

dim check_zhuchiren, edit_zhuchiren ' 关于主持人的几个变量
check_zhuchiren = Trim(Request.QueryString("check_zhuchiren"))
edit_zhuchiren = Trim(Request.QueryString("edit_zhuchiren"))

dim check_time, combo_year_lower, combo_month_lower, combo_day_lower ' 关于时间的几个变量
dim combo_year_upper, combo_month_upper, combo_day_upper
check_time = Trim(Request.QueryString("check_time"))
combo_year_lower = Trim(Request.QueryString("combo_year_lower"))
combo_month_lower = Trim(Request.QueryString("combo_month_lower"))
combo_day_lower = Trim(Request.QueryString("combo_day_lower"))
combo_year_upper = Trim(Request.QueryString("combo_year_upper"))
combo_month_upper = Trim(Request.QueryString("combo_month_upper"))
combo_day_upper = Trim(Request.QueryString("combo_day_upper"))

dim check_keywords, edit_keywords ' 关于关键词的几个变量
check_keywords = Trim(Request.QueryString("check_keywords"))
edit_keywords = Trim(Request.QueryString("edit_keywords"))

dim check_caijiren, edit_caijiren ' 关于采集人的几个变量
check_caijiren = Trim(Request.QueryString("check_caijiren"))
edit_caijiren = Trim(Request.QueryString("edit_caijiren"))

dim check_string_to_find, StrToFind ' 关于要查询的字符串的几个变量
check_string_to_find = Trim(Request.QueryString("check_string_to_find"))
StrToFind = Trim(Request.QueryString("edit_string_to_find"))


' 定义要查询的数据库和表
dim DatabaseToQuery, TableToQuery
DatabaseToQuery = server.mappath("syl.mdb") ' 要查询的数据库
TableToQuery = "shengyuliao" ' 要查询的表


' 定义跟查询字符串有关的几个变量
dim StrSQL ' 总的SQL查询语句
dim sql_condition ' 用户选择的单个限制条件所构成的SQL限制条件
dim all_sql_condition ' 总的SQL限制条件
dim human_condition ' 给人看的单个条件
dim all_human_condition ' 给人看的总条件
dim human_sepe_str ' 给人看的条件之间的分隔字符串
' human_sepe_str = "<br>"
human_sepe_str = "  "


' 定义跟表单提交的字符串有关的变量
dim old_value ' 该变量用来存放除掉页码之后的提交字符串
dim new_value ' 换页的时候的新字符串
dim issubstr ' 该变量用来存放字符串检索的返回值

old_value=Request.ServerVariables("Query_String")
issubstr=instrrev(old_value,"&page=") ' 搜索提交符串中的子字符串"&page="
if issubstr>=1 and issubstr<=len(old_value) then
old_value=Left(old_value,issubstr-1)
end if


' 定义并且建立Connection对象和Recordset对象
dim conn
dim rs_str
set conn=server.createobject("adodb.connection")
conn.open "driver={microsoft access driver (*.mdb)};dbq=" & DatabaseToQuery
StrSQL = "select ID,编号,单位,栏目,题目 FROM [" & TableToQuery & "]"
set rs_str=server.createobject("adodb.recordset")

' 想使用记录集rs的AbsolutePosition属性,就要设定其CursorLocation属性
' 而且要在记录集没有打开的情况下进行设置,也就是在rs_str.open语句之前进行
' rs_str.CursorLocation = 3 ' 设置成3表示动态处理,在客户端处理,处理时要切断连接,完毕之后再恢复连接
' 设置成2表示在服务器端处理
' 设置成1表示在客户端处理
' 原本想使用rs_str.AbsolutePosition来取得当前记录在数据库的表当中的绝对记录号,这是行不通的
' 因为rs_str.AbsolutePosition表示当前记录在记录集当中的绝对位置(第一条记录的绝对位置序号是1)
' 现在看来,只有想办法利用记录的关键字段(例如,自动编号的字段ID)进行标示该记录
' 这段话跟本程序将要实现的功能无关,但为了记住这个知识点,暂且存放在这里


' 定义跟分页有关的常量和几个变量
const MaxPerPage=10
dim rec_count
dim CurrentPage
dim TotalPages

' 定义跟循环有关的几个变量
dim i,j
回复
fantiny 2005-04-12
需要贴出代码
回复
相关推荐
发帖
ASP
创建于2007-09-28

2.8w+

社区成员

ASP即Active Server Pages,是Microsoft公司开发的服务器端脚本环境。
申请成为版主
帖子事件
创建了帖子
2005-04-12 04:23
社区公告
暂无公告